PROGRAMMING IN HWIF 


Version 2.10 


February 3, 1995 


(C) Copyright Psion PLC 1994-95 


All rights reserved. This manual and the programs referred to herein are copyrighted works of Psion PLC, 
London, England. Reproduction in whole or in part, including utilization in machines capable of 
reproduction or retrieval, without express written permission of Psion PLC, is prohibited. Reverse 
engineering is also prohibited. 


The information in this document is subject to change without notice. 


Psion and the Psion logo are registered trademarks, and Psion, Psion MC, Psion HC, Psion Series 3, Psion 
Series 3a and Psion Workabout are trademarks of Psion PLC. 


TopSpeed is a registered trademark of Clarion Software Corporation. Intel 8086 and 80286 are registered 
trademarks of Intel Corporation. IBM, IBM XT and IBM AT are registered trademarks of International 
Business Machines Corp. Microsoft and MS-DOS are registered trademarks of Microsoft Corporation. 
Apple and Macintosh are registered trademarks of Apple Computer Inc. VAX and VMS are registered 
trademarks of Digital Equipment Corporation. Brief is a registered trademark of Underware Inc. Psion PLC 
acknowledges that some other names referred to are registered trademarks. 


6102 0016 03 


SSS SSS SS Se aa eee a) 


Contents 
TL Introduction: to! HWIf....ccscesscccccs-s0desciscocessveeseessedecsveusrececnacesoesshccoseivevewaheeegeag 1 
OVErVIEW .......ececececeesenes misiadasaissseicseled Seve cocsosatsesuebecs sueuse sds Mase e cee oe vertex 1 
Learning to program in HWif .............ccsccssccscnssccveccseusecetscesesonscssenseseess 1 
Comparisons with OPL/w and with OOP ............cccsccscescesssonsesvescecscsees 2 
Recognising Hwif function calls .............cccesscsseevesecssccsccscceccuscnscscaecesce 2 
The connection to the Window Server ..........ccccsscsecscsevscsccecesceeeecscssees 3 
The general shape of Hwif programs............sccsscsssccovecsccsccsessccesscecesseccecenss 3 
PrOCESSING/ EVENTS: coc iads Severe este tIo eee sce tl ance see ston tte tie ode uvecteccancactacteuesbe 3 
WVPES OT: OVENTS es otiaeerins eT nates Tiere se cee eke re ee re nace oo neecbacteeeeDhacsdecncdsedas 3 
Programs with event sources other than keyS ..........csscscsscsccssosessescereces 4 
Active and inactive event SOULCES ...........ccscccsccscoceececscecusasessscarsencncesecs 5 
Active words and status words ContrasSted .............cccccescsccscescsecscscsceses 6 
uGetKey and uGetKeyA compared............csssessescsscscsccsccscsccecssancucseeaces 6 
Deferred ProCOSSING’. :.)-ci. casexsvescrei rece rveduvessscevesviscteSomey nective ee eee 6 
Programs with more than one get-event lOO ............cccsecscsesescssccecscsees 7 
Diamond keys ccc ste, Sores tata ean eG eee eee ect rocven cece sen Ste tewuunnen veer 8 
Menu: bar interacthons...scs cscs usaseeesseveecedveessoestidoue dete tectes ohne Seth edei ces 10 
Where _cmds must Point ..............cccscccsccsssosccosceusteccsceevserteccascescncseves 10 
Where _mdata MUuSt Point .............ccscoecsececcsccsrcesesseccecscsccucusoesscesesnscs 11 
How to use UPresentMenus...........csccscoscsscsccusesscvecsceccecsececsscetaenessesans 11 
The ManageCommand routine .............s.cesccssccsescessucnecteceusresceeccecueaeees 12 
Changing menu bar contents dynamically ..............ccccceccscesovccseeceseceecscs 12 
Restrictions on valid accelerators .............cscossecescecscsccecccetscesscastescnsccns 12 
Grey underlining:...:::¢.c88: 5. ventric forte ee ee ee re Oo oriadan 13 
MERU POSITIONS e020 cecbo0nd bc CUTIVE STs. we tetean erate core sen ccvduceden tt ha lesaveaans 13 
PFESENTING GidlOGS 03.2... <0 s.svecacevesesveacss ec ebesecexeed tome eens Shoe ove Bee ite or envadae 13 
Checking for run-time €rrOrs .........:sccssccecessccesececesstsccesescceacsccrssaecerseces 13 
Items that can be added to Hwif dialogs................cccsecsceecsccssosescacceeeces 13 
Longer Choice lists ......c:.00sscceccasedevacevecustencess7ess A ore rs OOTTET EV weet ens 14 
Typical dialog USAC «...ciccccscccseseneveovacusacteacecodecsdeeh chen teete Oe vinessavensas 15 
Dialog, underlining .........::.cceccsccoessensscssceeconssoesces SMMMMNED clo vitlacsecescnrens 15 
FIGIDICiFIOGS 6.205 cnsvessceseceseverecadectsceBtork Sev s50 00 645.7 eee nc ctae Srevndece 16 
Help dialogs (an older alternative) .............ccccseceescceccvecceceseccuccecaucusceses 18 
Line editors and multi-line Editors ..............ccscccecsscsccccesscsescesatsecscecscucecscass 18 
Editing features SUPPOFted..............ccscssocsececcecescecvcessrcecssesssesoscecensacsecs 18 
PFESENTING*AN1SditOl s....5 6c 2iceelivscocvesevesechisesasccciseescersectecdcdecuvancvewcncies 18 
Applications with more than one @Gitor ..........ccssccescscosceeceseseccsceccesaveas 19 
Edit boxes and saved file Versions ............csscecsssesseecevcsccccsccevascssasateenas 20 
PRIME asars iovncsd senda caus cacevcdaidudete dustin es tos leaeddoccs SRN t Re FE, ORM ek oe oe 20 
Printing features Supported 3 co.c5..5. caccccccev Picea sstessevecsdvcenveducecivesd Sdeecencs 20 
The Print Setup dialogues. ss sssess ss cswess oc cise eros vee ok soe oe bat carer dete ast eeeledss 21 
The basic mechanism of printing ............ccccecssssccccenscsccceeusesaucecutececececs 21 
The PrintLine callback fUNCTION............ccccccceccesececescecucccesceeccsseeaecscnsaees 22 
The location of the print DUffer .............cccccecsveessccececscecscatetevcscseecesscecs 22 
The: Print: Details dialogs... ssccecisce.cvecec tetas sage aeveden boi deucesbuddvceettecessdees 23 
Word wrapping during printing...........cccscscsscscostevcecncucssusoccccecueeensensenss 23 
Printing the contents of multi-line Editors ..............cceececsessccscsenscsoenscnss 23 
Time-text, utility FUNCTIONS ....i66..cssceccsajeacscassascscecsecvesvecsceseccedesesvadeustvevacves 24 
Default textual representation .............cccscoccscsccsescccsceesscseuctecscscecenececes 24 
Refreshing the format on returning to foreground ..........cccsceccsseesseeecsesos 24 
Date/Time-text utility FUNCTIONS ...........ccescsecsccecestovsessuccecerscescouccesscosnsaccnss 25 
Hwif, the Console, and screen Output ...........cccceccesscececaccvcccceccscascncetacavenss 25 
Three options for graphics OUtPUt.............ccsccseecceccscceceeesecucesecuseeceecass 26 


Practical acquisition of graphics techniques ............ccccccecceceseuecscececeseess 26 


PROGRAMMING IN HWIF 


i 


Hwif opens the console channel ..............ssccssesscssscecseececconccavecsnsceeeens 27 
TGV AU WINKOW o.oo Seas deceuueccsuttcs os sates ues eoiBacetsansescoicoeeotue tek eras: 27 
GLEY corracsrretrsceeremtadenece tee secscrccone sec teasecas fre nacts vagensore ove forest sare see cree 27 
Dual mode applications.............cccseccccecsecsseccsssecccscassaccuscsntecesessuaceceusescess 27 
SLONING Gata tote ic. ccaxescnustecsicslvaasece¥ianstasieusen<tigicthisasbicem takes ac ack 28 
ThesDbf tile format tcc: secchsciesecs costes daa esvecg sats coesdsieescoeeeel esedea Aen 29 
Dialling telephone MUMDETS .............secssessevecsssucecccscccescucesssecceseecusenecs 29 
Communication with the System Screen ...........ccsscecsssosecoccsceccsssssssceceseeece 29 
Reading the command lime...............csesscssseoccurceccessensccntsecacseesevsccssvece 30 
Storing the name of the file currently Open...............cs-cosceceeccescecceuceescs 30 
The protocol of messages from the System Screen ...........cscseccsscesssesces 30 
SOME MOYES ON TUN-tIME EFTOFS..........ccccesccuccncscececsseecovccesccssceccscesccescevsnence 30 
SOME EFOrs tO COMSIGET ..........cccecescascsssecccecscaccvcesecatcncsccteeseeteseuccacens 31 
Strategies on handling eErrors..........ccccsccecsceecccssscescecucceececcescescestenceses 31 
Reverting to the previous file..............:csssscsescssccsescresccossovecsarsceseoescones 31 
Errors when formatting edit DOXES .........cccccescssceccesscovccecacceccescsescescnece 32 
Future developments. .ccc.eA tiesto Batre tastiest tca tee crt ete oc eeesicsouses 32 


2 Worked Examples in Hwit.............sccssscsrcsscesssscesscconcascersessconcostcacecaceusestaaceeece 33 
How to use the supplied examples ...........ccccsscsceececascescoccescscsccetsecesesseececs 33 
The embedded SUgGeStIONS............cscsecssecsccececsecsscessessecscescsstscessasenses 33 
Preview of the example applications.............ccccececesscescsccssscescecesceesecens 33 
Getting started: the Query application............cccccsscescsessseecevsscascescesseaneesces 35 
Hello Worl der. reco ws vissccascsave onaprreneserse tees ea SSS eaey Oe TOTES Oar Eee 35 
To build the program ..........ccceceee jaw nadea@ads vies Sel seislesicu.c hts soewecus sa deawaretons vee 35 
Errors: Guring-linking wssvecscvessceccvavesavecscressereer cere Corte rte wrt 36 
Running the application from the Series 3 System Screen ..........0ccescceeee 36 
Further explanation Of Qu1.C ........ccccsssssscsscscsseecssenscsecseasascestetescucensens 36 
Suggestions for MOGifying QU1 .............ccceeccosscesceesseceeccsccasccusseeesteases 36 
IntrOdUCING’ AGG «os caveneerdeienccbvconcciacesssiadigaresvaredsteslevsegess Aa loecastavaa sic 37 
Suggestions for Modifying Qu2 ...........ccccsescescsscesecsecssccucnsrcsteecececeusess 37 
A status Window and a MENU ba? ............cccscccececsecsccntecstscsceccnceseveseucs 37 
Defining: the: menu.Dar.:.....5. ea ae ee 8 es a 40 
The contents of ManageCommand...........ccccsscscsscsssccnscescectecseccesceeseess 40 
Suggestions for modifying QUS:..cicc224 wdsscee desoveve eeaes dette eoksesyh cs 41 
SUDPIYING ANSICON : 6.55 tsevcccsues ccecdeesssevacaasadts dove ccs Sc toee eR Oe Mes ee oa, 41 
First examples in presenting dialogs .............ccccecesecsesececcecestsscescesseasees 41 
Suggestionsformoditying*FileSizevrrsrrsrrstrt 43 
A date editor in a dialog ..............cscssccseessoscneceveuccccccessuecascnscancrescencess 43 
Choice lists and the time-text fUNCTIONS ............ccccscosccscsseccscsscescecescasss 44 
A note on the start-up heap ............:ssssccsssscascescessesccssccceseccarscesscastens 46 
Further COMMENtS ON CAUMP..........cccssccsscesceeccecscsccscerscsteecescesscaatensees 46 
Suggestions for Modifying QU ............ccccsssceccececccsscececercncossanscescescess 46 
FROMPAMGItO SAPP iicct.ccctescadesecececesosecesevecescuccssdedsadvslevciecncterecceetesrets 46 
The floating point emulator sys$8087.ldd .............ccccescscereecoceeccecssceceas 47 
Debugging a .app application .............ccccscccsceecsccsccuscascceecstcecacesuusence 47 
Some responsibilities of being @ .ADP...........cccccescvecsecsecscscssesceecesessuees 48 
Menu command look up - by accelerator or by index?..........-ccccsscesceneees 48 
Example of floating point Editor ...........cccccccecscescsccscsescecssassvcencetencecens 50 
Example of Numeric Editor ............cceccsscnscescecactecsecceccecscasoscusenranceeenens 50 
Examples of other dialog items .............c2sssecescsscssceusccsccucuscasseeccascnscess 51 
Suggestions for Modifying QUETY.............:cscsecoeseccensceeseeccscessscceccuscees 51 
Getting serious: the Tables application.............cccccsccsccsccercecocssasssconsascnceecs 51 
The state of the application...............c:sesccsccesscsecesseccescucsensauseecesanceeuess 52 
Using an environment variable.............ccsccsssscuccecsccessescecscnsescaececaesereas 53 
Memory consumption by environment variables ..............cccecoscerescescecees 53 
Complications on reading environment variables ............c.ccscececececseececes 54 
Suggestions for enhancing Tal ............ccccscessconccceccecsccesvessstensceseecececs 54 
Laying out information on the SCree@M..............cccscsecoucecseccucscecssaeusaeseses 54 
Laying out an action button and its associated text ..........ccceesesecerceereres 56 
Positioning an action button vertically ...............ccccceecscocseecscsececscsccsones 56 


CONTENTS 
— SSeS 


Animating the action Dutton ............cccscssceescuseevscececsecsecscessenceaceevscenses 57 
Suggestions for-modifyingala2s!.c:fcscees. veer het hice Mc oeeens 57 
Presenting, ancedit boxtreseerts: OM oat. a da.ccssstleeevies coves sierdtaieceh i thocicads 57 
Generating random nNUuMbETS..........ccccscsssccuscesecceccescnsccecacceccestessavcaseess 58 
Eurther comments Ongliagve.08 fetvccecdetcccess Meh carecuas eee MN eS soc 58 
Suggestions for Modifying Tad .............cccseccssecssesccsseecscescasctsesceeseseacs 59 
AOOINQUINFAPEIMEL cnaeereeei ys ceavasscecsk, Alasessasses teen an caltt co teeaieovecee esate. yee 59 
Drawing the: bargauges....tretc tous! sac ccinccsatccasenes scocicct ae daccdlursearceetcoues 60 
Limitation on debugging Tables...............cssscccsesscsecesecceccnscacensceencencacs 61 
Suggestions for enhancing Tables ..............ccssssseccsscccsccsscccesscureseascens 61 
The remaining example applications ...............cesecescosscevccssseccstacecuseeceuceecess 61 
Resource file access with REMIND ...............scsssssccsesecsscescsccecccovscceces 61 


3 Advanced Use of Hwif ..........ccsccosssecssscocesousncccsscccecsconesacnsstsccascrececesacsecessenece 63 
BuIGing the HWit DCAry cas sadcssusincx teste. a te tencSeTeeeel ws ce Ot Mb Sov ccs 63 
EXtending Witte: circu teee eae t iret te tecert ., dacs nee ete este tet a ID Fea ca ace 63 
Combining Hwif with object oriented code ............cccccccsccsececcecesccascesccceesecs 63 
Wsingia Se pardre OVE orcs errant cen wid sasyurcosinaccnts vost otastswiues secucoomeaesesn 64 
Debugging*an HWIMDY Wirores..c.-caccucsevesaneatesacisssiseddess gh naswswsre stun tics cis 65 
Modifying the HWif category ..........ccccsccseccseecesccsscecsescucescessessesasescenus 66 
Access to a growing scroll bar from Hwif ..........:cccccseccsssseccececcesseccesceseusease 67 
The application's category TilG acest Me escancsuar sie ccecs te ansee ees bone tacn eee ti ascciie 67 
C_DONEWN items in dialogs ............ccccseccssccccavsccesecscescsccsccosseeseuceensce 68 
The SE_DONEWN struct and the WN |_SET method of DONEWN ............. 69 
The/BAR AO ractiverobject cnt tecvcsccivelscsurssisdcccecsattcen cto 69 
Termination of the grow bar dialog...........cccsssssscccesecsssesesecesscsssseneeesens 70 
GOmeralMCOMMeMtS cer. cron stacc tarde stac tsa tercuieties mest ccatcomccss tyes tuce hc vhewel«, 71 


4 Hwif Reference Documentation................scccscsssssscsssccsasccasccossccsncesccacecsscecsesees 73 
Overview of the Hwittibrary esraicsyuiestexcasaves tt re tineaddaneyencvveeevecivaccisadeeiveeis 73 
Two levels within the h-layer Calls..............csssccsessccscsnsccescesssevscaseasccecs 73 
Two layers within the u-layer calls .............cccccccseccessccsecsecersccsseaeceeecacs 73 
Groups of functions and naming CONVENTIONS...........c.ccsscssceseccsercnscescece 73 
Return values and error notification ..............ccscseseccecsccecesccssccscecscascece 74 
Binary CountedrStrings: tri. Scticr. core ecccecesncceonccdsdeeccs uecusievccsscscductes 74 
Dialog and menu interactions as a special Mode ..............csccsesccsscenccecees 74 
The central functions in the u-layer Of HWif..............ccccccccscoccscecescarescvececses 74 
Initialisation common to Hwif applications (uCommonlnit).............c0ccses 74 
Enable use of grey (UEmableGrey) .............cccccescssesccsscessceveecessseesceceneees 76 
Read a key, waiting for its delivery (UGetKey) .............ccccsscssceecasteecescecs 77 
Read a keypress asynchronously (uGetKeyA)............-.-cecesesscoscsseeseccace 77 
Cancel outstanding asynch’ keypress request (uCancelGetKeyA) ............ 78 
Test if a keypress is outstanding (ukeyPressOutstanding) ..............csee0006 79 
Convert accelerator into command index (uLocateCommand) .............0068 79 
Present a set of menus (uPresentMenus)...........ssccsesesecesesccescsscescnsrscacs 81 
Open a dialog (UOpenDialog) ..............ccsccsssccesessceuceeccesescseccnccesneceucecs 82 
Run a dialog (URUNDial0g) ...............:0ccceeccesccusenceeestaceevsceseccuscesceecuswass 83 
Dialogs and flashing CUISOMS ............ssscesecssscssccosecassovecesseceseuscesceecuteees 83 
Add an action list to a dialog (uAddButtonList) ...............scecccessesscecececees 83 
Add a choice list to a dialog (UAddChoiceList) ..............ccccecsseesecssececscecs 84 
Add a general item to a dialog (UAddDialogltem) ..............cccecscaccsesceeeess 84 
Text items in dialogs (HEDIALOGYIEXT) ©. SM Neleawtietecdescunticvenconss 85 
Numeric editors in dialogs (H_DIALOG_ NUMBER) .............scsssssssssseesseees 85 
Floating point editors in dialogs (H_ DIALOG MELOAT) ie ttvecedssasteetedeeseswees 85 
Time editors in dialogs (H_DIALOG TIME) .........cccccccecececeecesssssseseveeseees 86 
Date editors in dialogs (H_| “DIALOG MDA WE et iipncc at tity eecen dM vanteveeetixc: 86 
Useful date:constants.........ces0seccseoftees cs Mtevete anes cosas sgcedes esas ectvebecvewsne 86 
Non-scrolling text editors in dialogs (H_DIALOG_EDIT) .............:.cseeceseees 86 
Scrolling text editors in dialogs (HEDIAWOGFSEDIN? <0 cec terest escaieotieues 87 
Secret data input items in dialogs {H_ DIALOG _XINPUT). ........0ssessseesveess 87 
Filename selectors and filename editors (H_ DIALOG FSEL). ..............0066 87 


iii 


PROGRAMMING IN HWIF 


iv 


Begin a dynamic choice list (UBeginDCL)...............sccecsesecsecesscecscsceeesees 88 
Add a choice to a dynamic choice list (UGrowDCL)............c.ccscsceesececeoes 88 
Add a dynamic choice list to a dialog (UAddDCL) ...............0ccccescecescscees 88 
Underline menu items with a grey line (uAddGreyUline) ..............csceeeeees 88 
Underline dialog components (uSetDialogUline) ..............cccssescerecccecceceee 91 
The auxiliary functions in the u-layer Of HWif ..............:ccescecesccscescesccceccscees 92 
Disable/enable Escape processing (UESCape)............scecococcscsorsccesceccceses 92 
Find ID of main window (UFindMainWid)............ccccececscssresceseeveccscsceenes 93 
Force the application into foreground (UForceTOFront) .........ccccsesesesceeeee 93 
Fail-safe presentation of an error string (UErrorString) .........ccscessseceseseeee 93 
Fail-safe presentation of an error message (UErrorValue) .........0sceseseevenee 93 
Check that a handle is non-zero (uCheckHandle) ............ccccccsecenscsovececes 93 
Convert a ZTS to a BCS (UZTStOBCS) .............cccccecececovesscvcecscecsseeeseess 94 
Present a menu-like dialog (UDialogMenu) ...............ccscssesscvcvcvcvescscaceess 94 
Present a dialog containing textual information (uDisplayText) ............... 94 
Time-text utility FUNCTIONS ..............cecsceccescenscscaveccteceescecssesscecasscsaseceseceses 95 
Open a time-text channel (HTTOpen) ...........sccsccscscsescscsccscssessvcverensceces 95 
Set time-text abbreviation lengths (hTTSetAbbreviations) ............cseseseees 95 
Set time-text format (HTTSetFormmat) ...........cccccsnscssecssccecseccssecsenseseseecs 95 
Set time-text time and date (HTTSetTime) ............cesecsccsecscsvcssscecscecncens 96 
Sense string from time-text channel (hTTSenseString) .........ccceceessceceeees 96 
Close a time-text channel (HTTClose) .............cccesesescececsovsvcteccveesseseeecs 97 
Stand alone date/time text editor FUNCTIONS..............cccecscesevensecesccesecncseceess 97 
Create a date/time text editor (HDTOpPEN)..........:csecccesccsnscsvccsssceccsseceses 97 
Close date/time text editor (HDTClose) .............cccecssuccecsveovcveccscecsceseres 97 
Set value in date/time text editor (HDTSet).............ccscsecseccvsscccveececessees 97 
Retrieve value from date/time text editor (hDTSemse)............ccscsescseseves 97 
Date/time text editor self-check (hDTSelfCheck)............cccssssecceceecseececs 97 
Pass key to date/time text editor (hOTHandleKey) ................ccecesceesceeees 98 
Emphasise date/time text editor (hDTEmphasise) ............cccscseossceecssseece 98 
HDT xxx structs:(H: DTEDEM) icc. ccctascsesnseucissechevdgeessacdeseveteceecevossecneceses 98 
hDiExxx structs: (HESE. DGEDIM).w.cciveibsesccnsicuscducnecchccereuesstscaveavevarnsves 99 
Edit: DOX:fUNCHORS sys ccudies: Ricees tree Suerte he ete ence cette tt otrcscet conaarmnne 99 
Open an edit box (NEBOpen) ............cccceceessscsecerestscotsescaucescecaseusaveveees 99 
Pass a key to an edit box (hHEBHandleKey).............cscscscsesscccescssececcessees 100 
Formatting in: bBaACKQroUNG :......cscseassteeccunsasskewacet fo0Ms cc ta cade eS baceve cous ves 100 
Complete edit box formatting (hEBCompleteFormat) .............csesscssereseees 100 
Sense the text in an edit box (REBSenseText)............cscecesesecscscescecescees 101 
Set the text in an edit box (HEBSetText) .............cccscessescsscesescuscnsesescess 101 
Emphasise an edit box (HEBEmphasise)............csscccscsssesessccccecscucesenceces 101 
When edit boxes lose their CUrSOF ............ccscescscecscecessstoceesccoussscssereeees 101 
Set edit box select and cursor (HEBSetSelect) .............cscesceccssecscecececeses 102 
Sense edit box select and cursor (hEBSenseSelect) ..............ccecseesscereees 102 
Sense edit box clipboard contents (hEBSenseClipText)..........ccccscessccsrees 102 
Set edit box clipboard contents (HEBSetClipText) ............csccecsccssecsesneens 102 
Change width of edit box (hEBChangeWidth)................csccecscscscccovecesees 102 
Set width of edit box cursor (HEBSetCWidth) .............scccceecccevseeeesseseees 103 
Insert text buffer into edit box (HEBInSert)..............ccccscsceccsccecscncsseeeees 103 
Replace selection in edit box (HEBReplace) ................ccsccecoseressecesscseeees 103 
Evaluate edit box expression (HEBEvaluate) .............ccesescessescscscecsceeseees 103 
Copy function for edit box (NEBCOpy)............csscscesscecscsscescetscecscsencesens 103 
Paste function for edit box (HEBPaste) .............ssesssscveccssecsccteseseeseeeeees 104 
Find text within edit box (HEBFind).............ccscssssccesccscecccsaceccueseescunses 104 
Clear edit box changed flag (hEBClearChanged) ............:sssesesececeesceseacs 104 
Sense if edit box contents have changed (hEBSenseChanged)................ 104 
Hide or show edit box symbols (hEBShowSymbols) .............cecscscsseeeeees 104 
Close an edit box (HEBCloSe)...............scccccssrecatasccestescecsesvseerecesresscncs 105 
Return handle of edit box document component (hEBSenseDoc) ............ 105 
Set capacity of document component (HEDCapacity) .............cssceceseaeeees 105 
Insert text into document component (HEDInsert).............sesecssecesseeceenes 105 
Notify edit box that document has changed (hEBDocChanged)............... 105 
Set word-wrap margin (NEBSetMargin) ...............scscecerecseseceseseeecesesenes 106 
Sense word-wrap margin (HREBSenseMargin)..........scccescesssereceescecncererees 106 
Convert position to line number and pixel offset (hREBPosToXL).............. 106 


CONTENTS 
—_—_—_—_—_—_  eeSeSeeSeFeFeFFFssFFSSSSeSSeSeSeSsSseF 


Printing and print Support FUNCTIONS ............cccccsecccsseseccesssecenessensscceceescoese 107 
Invoke Print Setup dialog (hPrintSetupDialog)...............ccssseseccosecseesescees 107 
Invoke Printer Configuration Setup dialog (hPrinterSetupDialog).............. 107 
PHNE Gata AiRrinth:22-vveracancsencevonssterac duaete cen crept tvecctacssusssMeg ies lacadven. 107 
Word wrapping during printing.............cccssssccssssseccsesesccesccseescseseeseceess 108 
Page breaks uring: PrINthd Acscct-ct 275sssaceseuseDicdcobeesasdberscndecs cocorscowce tics 108 
Limitations during the PrintLine callback .............cccccccosececcoseceecccecscnesece 108 
Set subsequent indent for printing (hPrintSetSl) ...............ccccesecesscoesseeee 109 
Sense page width for printing (hPrintSensePageWidth) ................secccceeee 109 
Sense printed width of buffer (hPrintSenseBufWidth) ...........c.cs.ccceesecceee 109 
Advanced possibilities when printing .............cccccccssccsseccsesccccescesececceece 109 

MiSCellaneOUs FUNCTIONS 5.02055 stand de eels poadeadecaimatatewsade dived aesincdoscncliccwaeee: 109 
Crack the command line (hCrackCommandLine) ..............cseccsesceseceesecoee 109 
Notify a change in the name of the file (hSetUpStatusNames) ................ 110 
Ensure that the path exists for a filename (hEnsurePath)...............0..c008.. 110 
Position a dialog (NDIgPosition) ..............ssccscseccecsesscecseccecsescrescceseunaces 110 
Emit DTMF tones (HDTMFString)...........0ssscsssecesssssscccsseccesessueseueesencecs 111 
Check if database file is compressible (hisDbfCompressible)................0.. 111 
Access help engine (hHelpSubSystem)...............cccsecssecessscececnsececesseeecs 111 
Help: FESOUrCE STFUCTUPES .6...016icisiceestecocsnsdsaneserccnescsevsvasccnecsevecacicesccce 112 

PRELPEARRAY sncssadagauveedca rioters eigeducasauia seastansd Gueei dslanade the Caiawsehce 112 

STEIN Go ace sore cain atisios atacoseusedveasdaudts coras'etiehagttvientnes place. teen Ge ceae ices 112 

TORIC ARRAY cxicsec.fssatswts vecaxcacastelesond las sux ceessas layne t(lesaceueeues caus 112 
Pass handle of Appl.Res.Channel (hDeclareAppReb)................0seceseceeeses 112 
Open channel to resource file (hIMitAppReb) ..............sscccesecocccesceseesecees 112 
Request insertion of resource file pack (hRequestReplacePack) ............... 113 
Set system resource file language (hSetSystemResourceLang)................ 113 
Set choice list to use a variable array(hSetVarrayInChlist) ..............c0..e00 114 
Load applications'’s DYL(hLoadOwnDyl) .............ccccsescossecaseseeccsscssee sees 114 
Get last key press(hLastSystemKey) ...........ccccsssceccscccsccersscesccecssecerecees 115 
Call an object oriented dialog(hOODialog) ..............ccessescsssscccssccosscuscees 115 

The low level h-layer FUNCTIONS .............ccsescccsesccuccesseccccecensccesssencsececeseseees 116 
Menu bar interactions - OVervieW .........c.csccscsscccsescsseccrsccaceceecsessceseucens 116 
Dialog interactions - OVErViEW...........cssssesccessscccsseesenscesonssescusecensceusees 116 
Low level initialisation (HIFIMit) .............cccseccsssccessccosscessccencsenscoecesceusens 117 
Open a menu card (hCardOpen) ............cccccsescesececsceccsscusescescesceccenscece 117 
Add an item to a menu card (hCardAdd) ............ccccsccseccsscecscceesceseeseuscs 117 
Close a menu card (hCardClose)...........:sscscssccsesscccreccnsscnsscesccesccesucesens 117 
Open a menu bar (hMenuOpen) ............scccccssccccsseeccenscccrersceeecsenceunecees 117 
Add a menu card to a menu bar (HMenUAdG)...............-cceccesecasceccesseuccs 117 
Run a menu bar (HMenuRun) ............cssececsscesesecsseccvscctscceueccesceeseescaseus 118 
Close a menu bar (hMenuClose).............sscccssscssseccessctseccnsesesceaccessnecees 118 
Opera dialog: (HDIGOPeN) sacks sesscscenasseqsacane5, o's oneeddeusucusdesuenatedeadycet bs 118 
Rui a dialog ARDIGRUA): visnnsin--svcarcsecsusseues suena dohssian ghuevaawncccdaxhoatdicorses 118 
Close down an incomplete dialog (hDigClose) .............cccsesecceseccseceecseese 118 
Add an item to a dialog (HDIGAd)...............cscsssccccnssecessscecnssececccesceenes 118 
Open a choice list (hChoiceOpen) ...............sceccssceccussceseccssecesceseceeceaceas 118 
Add an item to a choice list (hChOic@Add) ..............cesscccseccescceccescecseuces 118 
Close an incomplete choice list (hChoiceClose)..............cesccoscssseceesevenecs 119 
Open a button action list (hButtonOpen)..............:cssscsssccssscceceesceceescees 119 
Add a button to an action list (hButtonAdd).............ccccsseccsescseceeceeceeeses 119 
Close an incomplete action list (hButtonClose) ...............ccssccsecescesccescaee 119 


CHAPTER 1 


INTRODUCTION TO HWIF 


LEE a ee ee SR 
Overview 


Hwif, the Handheld Wimp InterFace, is a library of user-interface routines for C programmers writing 
applications for the Psion Series 3, Series 3a and Workabout. At the time of writing, no comparable 
library exists for the HC or MC ranges of SIBO computers. 


In this manual, references to the Series 3 should generally be taken also to refer to the Series 3a and the 
Workabout. Cases where there are differences in behaviour between the various models are mentioned 


explicitly. 


Hwif makes it easy for programmers to present menus, dialog boxes, and edit boxes. Hwif also allows 
programmers to tap into the abundant printing functionality of the Series 3 - and much more, beside. 


Hwif is an interface library because it provides access to code that is already present in the Series 3 
ROM. Applications which include calls to Hwif will find that the size of their code only increases 
slightly as a result. This is in marked contrast to the case if an alternative user interface system is 
developed. At the same time, the ROM-based user interface is likely to be more comprehensive and 
robust than any such alternative user interface. Finally, users will instantly feel at home with the user- 
interface presented by Hwif applications - since it is the same as that possessed by the applications built 
into the Series 3. 


Going beyond the issues of dialogs, menus, and edit boxes, Hwif assists programmers in writing 
applications that conform to the wider responsibilities of applications on the Series 3 - in terms of 
communicating information to and from the System Screen and the status window. This adds to the effect 
of applications which behave just like the built-in ones. 


On a more practical issue, Hwif simplifies many of the initial programming choices facing would-be 
Series 3 programmers. One particular method of interfacing to the Window Server is selected over all 
others, and one particular method of structuring responses to external events is highly recommended. 
These choices out of the way, programmers can concentrate, in the meantime, upon acquiring familiarity 
with many of the other aspects of the Series 3 ROM software. In due course, programmers may wish to 
re-evaluate the choices made for them by Hwif, and may choose to take alternative decisions (for 
example, a different interface to the Window Server). In the meantime, Hwif helps programmers get off 
to a flying start. 


Learning to program in Hwif 
The documentation for Hwif consists of three chapters: overview, worked examples, and reference. 


= The current chapter gives an overview of the scope of Hwif, and of the basic programming ideas 
it embodies; generally speaking, this is the chapter that should be read first 


= The chapter Hwif reference guide presents a factual documentation of all the functions available 
in the Hwif library; generally speaking, this is the chapter that should be read last 


= The chapter entitled Worked examples in Hwif contains a more discursive account of how these 
library functions can be used, in the context of a series of example programs. 


The associated example applications are an integral part of learning to program in Hwif. As well as 
illustrating access to the built-in menu and dialog system, they also contain many demonstrations of how 
to use other, wider parts of the rich ROM software the Series 3 provides. To start with, the examples are 
built up in stages, to make them easier to understand. The very first example is built up in stages right 


PROGRAMMING IN HWIF 


from the very beginning (an Hwif version of the Hello World program), with ample discussion of such 
topics as programming environment, and how to "build" ("make") an application. The later examples 
embody considerable sophistication and an extensive use of the Series 3 ROM software - enough to keep 
even the most avid of would-be Hwif programmers busy for quite some time. 

Comparisons with OPL/w and with OOP 


There are in fact three choices for programmers wishing to hook into the Series 3 built-in menu and 
dialog system: 


= Write in OPL/w, the Wimp extension of OPL 
# Write in traditional C, using routines in the Hwif library 


= Write in Psion's proprietary object-oriented extension of C, interacting much more directly with 
the object classes in the ROM. 


The advantages of OPL/w are: 


= OPL programs can be written and debugged on the Series 3 itself, without any additional 
hardware or software tools 


= OPAL is a protected environment, automatically handling runtime errors so that they do not cause 
an application crash (or even a system crash) 


= Some programmers find the syntax of OPL less intimidating than that of C 

= OPL provides a convenient layer over the raw database file functionality provided by the OS. 
On the other hand, writing in C has the following gains: 

= The code executes more swiftly 

= Cis a richer programming environment, with abstract data structures, pointers, and typedefs 

= It is generally much simpler to call routines in the OS from C than from OPL 

= C allows access to a wider range of printing and editing support utilities 

= Programmers with experience of C have no need to learn OPL 


= Code written in C for other products on other hardware can obviously be converted more 
quickly into C for the Series 3 than into OPL for the Series 3 


= Conversely, code written in C for the Series 3 is more likely than OPL code to have parts that 
are portable fo other projects; in this sense, programming in C is a better long-term investment. 


As far as functional access to menus and dialogs goes, there is little of substance to choose between 
OPL/w and Hwif. For greater control over menus and dialogs, programs have to be written in object- 
oriented C. This is discussed in its own manual. 


Compared to the full object-oriented approach, Hwif is a significantly simpler programming system; 
nevertheless, it allows the creation of powerful and attractive applications. As evidence of this, a series 
of realistic example applications accompany this manual, each written using Hwif. These applications 
are all serviceable utilities that are likely to be found useful, either as they stand, or after an element of 
customisation. But they by no means exhaust the scope of what can be achieved using Hwif. Indeed, each 
example is deliberately constructed in such a way that an enterprising developer could enhance it 
considerably. To this end, the discussions on each example contain numerous suggestions as to how the 
application could be modified and extended. 


Recognising Hwif function calls 


Hwif library calls all have names starting either with lower case u or lower case h. For example, 
uCommoninit, uGetKey, hPrintSetupDialog, and hDlgPosition. 


In the code fragments below, routines supplied by the application have names starting with upper case 
letters. For example, specificinit, MainLoop, and ManageCommand. 


Routines with names starting with lower case w or lower case g are part of Wlib, the Window Server 
library. Plib functions have names starting with p_ (or sometimes f_ or Dbj). 


1 INTRODUCTION TO HWIF 
SSNS 


The connection to the Window Server 


The type of connection made by Hwif to the Window Server is actually via the console device (discussed 
further in a later section). Although this will be of little concern to most Hwif programmers, several 
points about the nature of this connection should be stated (particularly for the sake of those 
programmers already familiar with the contents of the Window Server manual): 


= The connection is made w_CONNECT_DISABLE_LEAVES - removing the need to enclose WIib calls in 
p_enter harnesses. When an error occurs in a Wlib call (such as lack of system memory for the 
request to be carried out), a simple error value is returned, rather than a call to p_leave being 
made. 


= The connection is made W_CONNECT_PRIORITY - so that the priority of the application is 
automatically adjusted, as standard, whenever the application passes into foreground or 
background. 


= The connection creates a window the full size of the Series 3 or Series 3a screen, with its own 
backed bitmap - so that Hwif programmers have no need to concern themselves with processing 
redraw requests. Note that the Series 3a screen can emulate the Series 3 screen. 


————E EEE ene SS eee 


The general shape of Hwif programs 
All Hwif programs can have the following for their main routine: 


GLDEF_C VOID main(VOID) 
€ 
uCommonI nit); 
SpecificInit(); 
MainLoop(); 
> 


The library routine uCommeninit performs initialisation that is common to all Hwif programs, such as 
connecting to the Window Server, and creating control blocks for subsequent menu and dialog 
interactions. A routine such as SpecificInit would be supplied by the program itself, to carry out 
initialisation specific to the particular application, prior to getting down to the real business of the 
program. Finally, a routine such as MainLoop (also supplied by the program) is where this real business is 
handled. 


Hwif programs can usefully be viewed as consisting, in their steady state, of a series of responses to 
events. Accordingly, the routine MainLoop can always have the general shape 


LOCAL_C VOID MainLoop(VvoiD) 


{ 

FOREVER 
€ 
GetEvent(); 
ProcessEvent(); 
} 

> 


More flesh is placed on this skeleton in the sections immediately following. 
The program terminates in response to suitable user-input, probably with a call 
p_exit (0); 


inside a routine such as ProcessEvent. 


———SSS—_SS—S EE ee a a 
Processing events 


Types of events 
Among the types of events that a program might process are: 


= menu command hot keys, of the PSION+X variety 


PROGRAMMING IN HWIF 
SS 


= other keys with special meaning for the program, such as cursor keys or alphanumeric input 


= system messages such as notification of passing into foreground or background, or the Series 3 
being turned off and then on again 


= messages from the System Screen for the application to close down, or to change the file 
currently being used 


= the expiry of timers or alarms 
e the receipt of data via the serial channel. 
These events can in fact be classified into just two types: 
= those received as a result of reading a keypress 
= others - of which only the last two in the earlier list count. 


The point is that foreground and background messages, on the one hand, and messages from the System 
Screen, on the other, are both received by Hwif applications as special sorts of keypresses (ones with 
specially recognisable values of the keycode). (What is going on here is that the console device is 
converting all Window Server events to otherwise unused keycodes). 


This leads to a particularly simple form for the routine MainLoop: 


LOCAL_C VOID MainLoop(VOID) 


€ 
WMSG_KEY key; /* to receive key press */ 
FOREVER 
€ 
uGetKey(&key); 
if Ckey.keycode&W_EVENT_KEY) 
€ 
zee /* process system message */ 
else if (key.keycode&W_SPECIAL_KEY) 
{ 
ane /* process menu command hot key */ 
else 
{ 
iaia /* switch statement on other keys of interest */ 
> 
3 


The routine MainLoop can have this form for all applications in which there are no events to process, other 
than those received as keypresses. 


An application can of course omit the test 
if (key. keycode&W_EVENT_KEY) 


if it has no special action to take on passing into foreground or background, or on the Series 3 being 
switched on, or on receipt of any messages from the System Screen. 


An application can likewise omit the test 
if (key. keycode&W_SPECIAL_KEY) 


if it has no menu bar (for example, if it is only using Hwif in order to access its dialog functionality). 


Programs with event sources other than keys 


Consider a program in which there are events other than keypresses. For example, a Spy program giving 
information about all applications currently running on a Series 3 might update its display regularly, on a 
timer - to ensure that the display keeps up to date with what is happening in all the different applications. 


In such a case, the program cannot know in advance, at any one time, which of the two events will occur 
first: the receipt of a keypress, or the expiry of the timer. 


1 INTRODUCTION TO HWIF 
oS eS 


Accordingly, the synchronous call uGetkey must be replaced by the asynchronous call uGetKeyA. This 
latter call takes an additional parameter - the address of a status word that is written to when a keypress is 
in due course received. If on the other hand the timer expires without a keypress being received, it is the 
status word of the timer that is written to. 


The status word is set to E_FILE_PENDING when the call uGetKeya is made, and is set to zero when there is a 
keypress ready to deliver. Maintoop in this case acquires the following form: 


LOCAL_D WORD timstat; 
LOCAL_D WORD keystat; 
LOCAL_D WORD keyact ive=FALSE; 


LOCAL_C VOID MainLoop(VOID) 
€ 
WMSG_KEY key; 


FOREVER 
€ 
if Ckeyactive) 
wFlush(); /* flush any outstanding graphics calls */ 
else 
€ 
uGetKeyA(&keystat ,&key); 
keyact i ve=TRUE; 
> 
p_iowait(); /* wait for something to happen */ 
if Ckeystat==E_FILE_PENDING) 
€ 
aon /* the timer must have expired */ 
else 
€ 
keyactive=FALSE; 
a /* proceed as above */ 
> 
> 
> 


This is more complicated than the preceding version (in which there is only one event source) in each of 
two ways: 


= the single line of code with the call uGetkey has been replaced by a series of lines that calls either 
uGetKeyA or the Window Server routine wr lush, and then in either case calls p_iowait 


= — the code that determines the action appropriate to the keypress that has just been received now 
has to stand alongside additional code that determines, by means of tests on status words, 
whether a keypress has indeed been received, or whether it is another sort of event that needs to 
be processed. 


An application with more than two event sources will have a correspondingly enriched set of tests on 
Status words, in order to find the event source which has delivered an event. On the other hand, there is 
no further complication over the GerEvent part of the routine - this remains as in the above example. 
Active and inactive event sources 


Note that applications using the asynchronous call uGetkeyA need (on pain of being panicked) to keep 
track of whether they already have a so-called outstanding read for a keypress. In the above example, 
this is handled by the variable keyactive: 


= every time round the main loop, the call uGetkeyA should be made only if keyactive is FALSE 
= keyactive is initialised as being FALSE 

= every time uGetKeya is called, keyactive is set TRUE 

= every time a keypress is actually received, keyactive is set FALSE again. 


Similar care must be taken for any other asynchronous event source. Thus the above program would 
probably have a variable timactive too (the code that primes the timer is missing from the above listing). 
In general, event sources do not take kindly to being asked more than once to deliver an event, without 


ees 
5 


PROGRAMMING IN HWIF 


OO -- rr eee 


an event being delivered in the meantime. This is regarded as evidence of faulty program logic, 
deserving of a panic. 


Of course, it would be possible to simplify the above code example, dispensing with the explicit 
variables keyactive and timactive: the call uGetkeya could be made at once, when a keypress is received, 
instead of setting the variable keyactive to FALSE. However, this is not recommended in general. In 
practice, as a program grows to contain more event sources, it becomes ever easier to keep track of which 
are active by using xxxactive variables, instead of by ad hoc program logic. 


The need to flush the Window Server command buffer at least once each time round the main loop also 
counsels in favour of locating the call uGetkeya as advised above. (Reading a key, whether synchronously 
or asynchronously, automatically causes the command buffer to be flushed. It is only when a non- 
keypress event has been received that an explicit call to flush the buffer is required.) 

Active words and status words contrasted 


Note carefully that the active word and the status word of an event source serve two different purposes: 


= the active word records whether a request has been made to the event source to deliver an event 
when one is available 


= the status word records whether an event has in fact been delivered 
= the active word is written to by the application itself, when it requests the delivery of an event 
= — the status word is written to by code other than in the application - for example, by a timer wait 
handler routine (when a timer expires), or by the Window Server process (when a key is to be 
delivered). 
uGetKey and uGetKeyA compared 
As may be surmised, the call 
uGetKey(&key); 
is effectively equivalent, in programs with no other event sources, to 
WORD keystat; 
uGetKeyA(&keystat, &key); 
P_iowait(); 


However, in programs with more than one event source, the call uGetKey will return only when a keypress 
has been received (this includes quasi-keypresses such as coming info foreground), whereas the call 
p_iowait will return when any event is ready to be serviced. 


Accordingly, what uGetKey strictly corresponds to is 
WORD keystat; 
uGetKeyA(&keystat , &key); 
p_waitstat(&keystat); 
since p_waitstat returns only when the event associated with the passed status word has indeed expired - 
regardless of whether other events expire in the meanwhile. 
Deferred processing 


An independent way in which the structure of a program's MainLoop can be developed is via a call which 
simply checks if a keypress has been received, without actually delivering it. In case no keypress has 
been received yet, the program might continue with some intensive processing, whereas if a keypress is 
outstanding, that processing might be deferred until the keypress has been dealt with. 


For example, suppose an icon is being re-positioned on the screen by cursor keystrokes, and that drawing 
the icon in its new position is time-consuming. To increase performance, a programmer might decide to 
draw the icon in its current position only if the user has ceased pounding on the cursor keys. Thus 


1 INTRODUCTION TO HWIF 
——_— Se eee 


UpdatePending=FALSE; 


FOREVER 

€ 

uGetKey(&key); 

switch (key. keycode) 
€ 
ek /* may set UpdatePending */ 
> 

if (UpdatePending && !uKeyPressOutstanding()) 
€ 
Drawlcon(); /* time consuming */ 
UpdatePending=FALSE; 
> 

> 


Programs with more than one get-event loop 


Many programs possess more than one mode. In different modes, various keypresses can have different 
meanings. Thus an icon designer program might have a special mode in which cursor keys Teposition a 
selected portion of the screen, whereas ordinarily, cursor keys might simply reposition the current 
drawing point. Again, a database program may have one mode in which records are being found, and 
another in which records are being added or updated. As another example, an agenda program may have 
one mode in which a month view is presented, and another in which a day view is presented. 


There are in fact two different approaches to programming in more than one mode: 
= have different get-event loops for each mode 


= — just have one get-event loop, and keep state variables to decide the appropriate response to 
various incoming events. 


To illustrate the first approach, consider again the case of an icon designer application which enters a 
special mode on receipt of a designated menu command. This could be programmed as follows: 


LOCAL_C MainLoop(VOID) 


{ 
FOREVER 
€ /* outer get-event loop */ 
1FGeSS) /* designated menu command received */ 
€ 
SetUpSpecialMode(); 
FOREVER 
€ /* an inner get-event loop */ 
> /* exit this loop on certain conditions */ 
TidySpecialMade(): 
> 
} 


In practice, the code responding to the designated menu command would probably be isolated in its own 
separate subroutine. 


Again, an agenda application with two different modes might in theory be structured as follows: 


LOCAL_C VOID MonthViewMainLoop(VOID) 


€ 
FOREVER 

a 

} /* exits loop only when user transitions out of month view */ 
> 


PROGRAMMING IN HWIF 
SS 


LOCAL_C VOID DayViewMainLoop( VOID) 


€ 
FOREVER 
€ 
> /* exits loop only when user transitions out of day view */ 
> 
LOCAL_C VOID OverallMainLoop(VOID) 
€ 
FOREVER /* start up in month view */ 
€ 
MonthViewMainLoopt); 
DayViewMainLoop(); 
} 
3 


To compare the two approaches to programming in more than one mode: 


= Programming with more than one get-event loop is generally easier: relevant state variables 
(such as might be initialised in a routine like setupSpecialMode referenced above) can be kept on 
the stack 


= However, it is only possible to go so far with more than one get-event loop; as programs 
become more complicated, just having one get-event loop becomes an ever better design 
decision. 


In particular, a program with more than one get-event loop may founder on account of the large overhead 
of maintaining shared common processing between the different loops. Thus testing for special events 
such as 


= a message from the System Screen to terminate the application 
= notification that the Series 3 has been switched on 
® the expiry of timers or the receipt of data via the serial channel 


could well be largely independent of which mode the application is in. As a result, logic would have to 
be duplicated at the tops of the various possible get-event loops. 


Incidentally, it is sometimes appropriate to disable some kinds of events when transiently entering a 
certain mode. Thus messages from the System Screen to terminate the application or to change the 
currently open file can be disabled by the simple line of code 


DatLocked=TRUE; 
in which case, in the System Screen, the user will be informed that the application is "busy" on any 
attempt to close it down or to change the file being used. 
Diamond key 
The following discussion on the diamond key applies exclusively to the Series 3a. 
In the previous section, the idea of programs possessing more than one mode was discussed. 
The concept is used in the built-in applications such as agenda and data. 


Commonly, applications switch to a particular mode when the corresponding key press event occurs. For 
example, the agenda built-in application switches to Week View mode when the PSION+SHIFT-+W 
keypress is received. 


On the Series 3a, the diamond key is used in the built-in applications to cycle around the various modes. 
Further, the built-in applications also allow the user to select which modes are to be included in the 
cycle. 


An Hwif program can also make use of the diamond key in this way simply by checking for the diamond 
key in its main event loop (or the appropriate event loop if more than one is used) and taking suitable 
action. 


If a status window is displayed, the icon can be replaced by a list of modes. Optionally, a diamond 
symbol can be used to highlight the mode which is currently active and can be a useful visual aid. 


1 INTRODUCTION TO HWIF 
eS 


This is achieved by using the window server functions wsSetList and wsSelectList (see the General 
Window Server Functions chapter in the Window Server Reference manual). 


The following simple example resizes the main console window and displays a permanent status window 
alongside together with a list of three modes. Pressing the diamond key successively, causes the position 
of the diamond symbol in the status window to be shifted to lie alongside “mode-B" and then "mode-C" 
and then back to “mode-A” again. 


Aside from the visual confirmation of a mode switch, the actual meaning and implementation of the 
switch is application dependent. 


#include <p_std.h> 
#include <wlib.h> 
#include <hwif.h> 


GLREF_D UWORD _UseFullScreen; 


LOCAL_D WORD keystat; 
LOCAL_D INT gc; 


LOCAL_D UINT modepos 

LOCAL_D TEXT * modelist{} 
€ 
"mode-A", 
"mode-B", 
“mode-C", 
3; 


et 
2 


LOCAL_C VOID ReduceMainWin(VOID) 
{ 
P_EXTENT StatusExtent; 
W_WINDATA wd; 


wiInquireStatusWindow(W_STATUS_WINDOW_BIG,&StatusExtent); 


wd.extent.tl.x = 0; 

wd.extent.tl.y = 0; 

wd.extent.width = 480 - StatusExtent.width: 
wd.extent.height = 160; 


wSetWindow(uF indMainWid¢),W_WIN_EXTENT, awd): 
> 


LOCAL_C VOID SpecificInit¢(VOID) 
€ 
ReduceMainWin(); 
wStatusWindow(W_STATUS_WINDOW_BIG); 
gc = gCreateGCO(uFindMainwid¢)); 
gBorder2(W_BORDER_TYPE_0,W_BORD_CORNER_4); 
wsSetList(3,&model ist [0] ,modepos) ; 
> 


LOCAL_C VOID SwitchMode(VOID) 
{ 
modepos++: 
modepos %= 3; 
wsSelectList(modepos); 
/* ... application specific mode switch code ... */ 
> 


PROGRAMMING IN HWIF 
——]——— KK eSSSSSSSsFSFSFSSSSSSSSSSSSSSSeeeSSSSSSSSSSSSSSSeF 


LOCAL_C VOID MainLoop(VOID) 
€ 
WMSG_KEY key; 


FOREVER 
€ 
uGetKeyA(&keystat, &key); 
P_iowait(); 
if (key. keycode & W_SPECIAL_KEY) 
€ 
key. keycode &= (“W_SPECIAL_KEY); 
if (key. keycode == 'x') 
p_exit(0); 
> 
else if (key.keycode == W_KEY DIAMOND) 
SwitchMode( ); 
> 
> 


GLDEF_C VOID main(VOID) 
€ 
_UseFul Screen = TRUE; 
uCommonInit(); 
SpecificInit(); 
MainLoop(); 
} 


Note that setting _UseFul Screen to be TRUE indicates that this code is intended to be run on either the 
Series 3 or the Series 3a. For a full explanation of the significance of the variable Useful screen, see the 
description of the function uCommontnit in the Hwif Reference Documentation chapter. 


SS EEE] _— SS eee] 
Menu bar interactions 
The way an Hwif application initiates a menu bar interaction is simply to make the call 
uPresentMenus() 
where there are no explicit parameters. 


The actual contents of the menu bar are communicated implicitly via the static variables _cmds and _mdata 
which various Hwif library calls expect to access. The application must ensure that these point to 
appropriate tables. 


In brief, _cmds is the address of a table of the text names and accelerators of all the current menu 
commands of the application, and _mdata is the address of another table giving the names of the menu 
tiles, and how many commands there are in each tile. 


Where _cmds must point 
For example: 


LOCAL_D TEXT *cmds[]= 
€ 
"nNew file", 
"oOpen file", 
"“aSave as", 
"iInsert", 
"eCopy", 
"fFrame off", 
"XExit", 
NULL 
3; 


GLDEF_D TEXT ** _cmds=(&cmds [01 ); 


This defines a menu bar currently with 7 menu items. 


10 


1 INTRODUCTION TO HWIF 


eee 
Note carefully: 
= The text for each menu item starts with its accelerator 


= The entire list is terminated by a NULL. 


Where _mdata must point 


For example, suppose that the above commands are split up into a "File" menu (the first three), an “Edit” 
menu (the next two), and a "Special" menu (the last two). Then the following definitions would be 
appropriate 


LOCAL_D H_MENU_DATA mdata[]= 
€ 
"File" 3, 
"Edit",2, 
"“Special",2, 
NULL 
3 


GLDEF_D H_MENU_DATA * _mdata=(&mdata[0]); 
Note that: 
= Again, the data is terminated by a NULL 
= The individual entries (one per menu tile) are given in the form of an H_MENU_DATA struct, which 
simply contains a TEXxT* followed by a UWORD. 
How to use uPresentMenus 


If the user cancels, or if an error has occurred (eg out of memory), uPresentMenus returns 0. Otherwise, it 
returns the accelerator of the item chosen. 


Accordingly, uPresentMenus is normally called in the following context: 


LOCAL_C VOID TryExecuteCommand(INT keycode) 
C 
keycode=uLocateCommand( keycode): 
if (keycode>=0) 
ManageCommand( keycode); 


> 
LOCAL_C VOID MainLoop(VOID) 
€ 
INT ret; 
FOREVER 
€ 
uGetKey(&key); 
if (key. keycode&W_SPECIAL_KEY) 
TryExecuteCommand( key. keycode&(“W_SPECIAL_KEY)); 
else if (key. keycode==W_KEY_MENU) 
€ 
ret=uPresentMenus(); 
if (ret>0) 
TryExecuteCommand(ret); 
> 
else ... 
> 
> 


That is, a menu bar interaction is initiated in response to receipt of a MENU keypress. The result, if 
positive, is dispatched to the Hwif library routine uLocateCommand, which converts the accelerator to an 
index into the current table of menu commands (as pointed to by _cmds). Finally, inside the routine 
ManageConmand, a switch statement (or equivalent) on the index is performed. 


Note that there are two routes to the routine ManageCommand: 


= the route via receipt of the MENU key and the presentation of the menu bar 


11 


PROGRAMMING IN HWIF 


= the route whereby the hot key is received directly, in the form PSION -+ ACCELERATOR. 


In the latter case, the bit w_SPECIAL_KEY is set in key.keycode, so that the user's presumed intention of 
invoking a menu command via its accelerator can be detected early inside any get-event loop. This bit has 
to be masked out before the contents of the keypress is analysed by the routine uLocateCommand. In case 
the user has typed PSION together with an accelerator that does not currently exist for the application, the 
routine uLocateCommand returns -1. Otherwise, as stated above, it returns an appropriate index into the 
table of menu commands. 


The ManageCommand routine 


The ManageCommand routine of an application is one of its most important ones, ranking alongside MainLoop 
and (to a lesser extent) main itself as the locus of the controlling logic of the program. 


For clarity, it seems best if the contents of ManageCommand just call other routines where the real work of 
each of the different menu commands is executed. This leaves ManageConmand as, primarily, an extended 
switch statement, branching on the command index number. 


Changing menu bar contents dynamically 
There are at least two methods of dynamically changing the contents of the menu bar. 


In the first method, two different sets of tables could be declared, and the values of _cmds and _mdata be 
changed when required. This might be appropriate for an application with two modes that differ 
distinctly from each other. 


Alternatively, for a more modest change, code such as the following suffices: 


cmds [5]=(show_frame? "fFrame off": "fFrame on"); 


Restrictions on valid accelerators 


On the Series 3, the allowed values for accelerators are the 26 lower case letters 'a' through 'z', together 
with the four arithmetic operator keys, '+', '-', '*’, and '/' (though some of the last four values may 
change to something else on a non-English keyboard version). 


On the Series 3a and Workabout, however, the allowed values for accelerators are all those allowed for 
the Series 3 plus the upper case letters 'A' through 'Z'. 


It is not possible to specify a menu command without an accelerator. This means that at any one time, an 
application is limited to 30 first level menu commands on the Series 3 and 56 first level menu commands 
on the Series 3a and Workabout. 


To handle upper case accelerators correctly, the MainLoop code fragment above could be changed as 
follows: 


LOCAL_C VOID MainLoop(VOID) 
€ 
INT ret; 
INT code; 


FOREVER 
€ 
uGetKey(&key); 
if (key. keycode & W_SPECIAL_KEY) 
¢ 
code = key.keycode & (“W_SPECIAL_KEY); 
if (key.modifiers & W_SHIFT_MODIFIER) 
code = p_toupper(code); 
TryExecuteCommand( code); 
> 
else if (key.keycode == W_KEY_MENU) 
€ 
ret = uPresentMenus(); 
if (ret > 0) 
TryExecuteCommand( ret); 
> 
else ... 


12 


1 INTRODUCTION TO HWIF 
SESS 


Grey underlining 


Built in applications have the ability to add grey lines undemeath menu items. This serves to group 
related menu items and can be a useful visual aid if used sparingly. 


Note that grey need not be enabled for the main console window in order for this to work. 

This feature is available on the Series 3a and Workabout, but not on the Series 3. It is discussed more 
fully in the Hwif Reference Documentation chapter. 

Menu positions 


The position of the menu item currently selected can be recorded by making use of the global variable 
_MenuPositions. 


This is particularly useful in an application where more than one menu bar is used. The position of the 
menu item selected in each menu bar can be recorded so that the previously selected menu item can be 
highlighted when a menu bar is re-displayed. 


This feature is available on the Series 3a and Workabout, but not on the Series 3. It is discussed more 
fully in the Hwif Reference Documentation chapter. 


SSS a re a a 
Presenting dialogs 
Presenting a dialog consists of the following steps: 

= one call to udpenDialog, to begin building up the dialog contents 


= one or more calls to uAddDialog! tem, uAddButtonList, and/or uAddChoiceList, to add items to the 
dialog 


= possibly, a call to hOlgPosition, to position the dialog to one side or comer of the screen 


= one call to uRunDialog, to await the user's response. 


Checking for run-time errors 


The programmer ought to bear in mind that an out-of-memory error can occur at any of the above stages, 
in which case the flow of execution must be terminated at once. In practice, this is very simple to do, 
since the Hwif uxxx calls all automatically inform the user if any error arises, and automatically clean up 
any temporary dialog resources that are no longer required. 


For example, to present a dialog with a title (Use) and a choice list (with prompt Clipboard): 


LOCAL_C VOID ChangeCl ipboardUsed(VOID) 


€ 
if (uOpenDialog("Use")) 
return; /* out of memory */ 
if CuAddChoiceList("Clipboard",&using, "1", 02", "30 140 NULL) 
return; /* out of memory */ 
if CuRunDialog¢)<=0) 
return; /* out of memory, or user cancelled */ 
WriteClipboardText(); 
} 


Items that can be added to Hwif dialogs 
These are precisely the same as in OPL/w, namely: 
= = choice lists 
® action lists of buttons 
= plain text items 
=# numeric editors 
® floating point editors 


= time editors and date editors 


13 


PROGRAMMING IN HWIF 


= text editors (both scrolling and non-scrolling) 
= secret data input boxes 
= filename selectors and filename editors. 


In most of these cases, the item is associated with a so-called live variable, which specifies the initial 
value of the item (when the dialog is made visible), and which may be changed when the dialog is 
successfully completed. The type of this live variable varies from item to item. 


Thus in the above example, the (global) variable using is the live variable for the choice list. 


Longer choice lists 


In some cases, the list of choices presented in a particular choice list may be too long for a choice list to 
be conveniently defined simply by one call to uAddchoiceList. 


The three functions uBeginDCL, uGrowDCL, and uAddDCL exist to help out with these so-called dynamic 
choice lists (the name reflects the fact that the contents of the choice list are built up over a few lines of 
code, rather than just being defined statically; in some cases, the contents in the list may change between 
different invocations of the dialog, to reflect changing run-time circumstances). 


For example, the following routine builds up a choice list whose contents are the twelve month names: 


LOCAL_C INT AddMonthChoiceList(UWORD *pmonno) 
€ 
H_DI_CHOICE ch; 
TEXT mon [32]; 
INT i; 


if CuBeginDCL(&ch)) 
return(-1); /* report failure to caller */ 
for (i=0; i<12: i++) 
€ 
p_nmmon(&mon [0] , i); 
if CuGrowDCL(&ch,&mon[0] >) 
return(-1); /* report failure to caller */ 
> 
return(uAddDCL("Month", pmonno, &ch)); 
> 


Alternatively, the function hSetVarrayInchlist can be used to build up a choice list, especially if the list 
might contain more than 255 items. 


For example, by modifying the above code, the same effect can be achieved as shown below: 


#define C_VASTR 6 
#define O_VA_APPEND 7 
#define OLIB_CAT 1 


LOCAL_C INT AddMonthChoiceList(INT item_no, INT *pmonno) 
{ 
UWORD used = 0; 
VOID *pvarray; 
TEXT mon{32J; 
INT i; 


if (uAddChoiceList("month",&used,NULL)) 
return(-1); /* failure */ 
pvarray = p_new(OLIB_CAT,C_VASTR); 
for (i = 0; i < 12; i++) 
€ 
p_nmmon(&mon [0], 1); 
p_send3(pvarray,0_VA_APPEND , &mon{[0] ); 
> 
hSetVarrayInChlist( item_no, (*pmonno), pvarray): 
> 


Note that error handling in this code is incomplete. 


14 


1 INTRODUCTION TO HWIF 
- eS eee 


Typical dialog usage 
Applications will in many cases wish to call dialogs in a loop, as follows: 


LOCAL_C VOID OpenFile(VvoID) 
€ 
H_DI_FSEL open; 
TEXT openbuf [P_FNAMESIZE+2] ; 
VOID *newfcb; 


SetupF i lename(&openbuf [0] ); 
open. flags=H_FILE_PICK_SELECTOR; 
open. fname=(&openbuf (0) ); /* initialise filename selector */ 
do 
{ 
if (uOpenDialog("Dump"')) 
return; 
if CuAddDialogI tem(H_DIALOG_FSEL,"File: " &open)) 
return; 
if CuRunDialog()<=0) 
return; 
openbuf [1+openbuf [0] ] =0; 
> while (TryOpenFile(&openbuf [1] ,&newfcb)); /* until open succeeds */ 
ChangeOverTo(&openbuf (1] ,newfcb); 
} 


Schematically: 


InitialiseLiveVariables(); 
FOREVER 
€ 
if (No memory for dialog) 
break; 
if (User cancels) 
break; 
if (Dialog choices validate okay) 
£€ 
PerformAction(); 
break; 
> 
> 


In such a case, relevant live variables need to be initialised before entering the loop. Then if the user 
mistakenly selects (eg) the wrong file from a directory, this choice will remain in the dialog when it is 
presented again (along with an appropriate error message), so that it is easy for the user to adjust the 
choice to what was intended. At the same time, users will be able to see what they typed into the dialog 
the first time. 


It is possible to discover the last key press and key modifers handled by the system on the application's 
behalf. This is particularly useful on exit from uRunDialog. 


A number of key press combinations cause a dialog to terminate; amongst others, they include ESC and 
HELP. Knowing which key press combination caused the dialog to terminate, enables the application to 
take appropriate subsequent action. 


For more information, see the description of the hLastSystemkey function in the Hwif Reference 
Documentation chapter. 
Dialog underlining 


Built in applications have the ability to add or remove a solid underline to components in a dialog. This 
serves to group related dialog components and can be a useful visual aid if used sparingly. 


Hwif programs can also do this by calling the function usetDialogULine and specifying the position of the 
dialog component within the dialog and indicating whether underlining is to be added or removed. 


This feature is available on all machines, although there is a difference in behaviour on the Series 3 as 
opposed to the Series 3a and Workabout. 


Further detail on this can be found in the Hwif Reference Documentation chapter. 


15 


PROGRAMMING IN HWIF 
—. Ka 


Help dialogs 


Hwif contains support for accessing the same Help engine as used by the in-built applications on the 
Series 3. 


The function hHelpSubSystem can be called in response to suitable key-presses from the user (eg the HELP 
keypress). 


The Help engine is resource-based and any serious user of hHelpSubSystem must create a resource file 
(using the tool RCOMP.EXE) containing resources defining the hierarchy of Help text. 


Typically, a MainLoop routine might contain the following code fragment: 


if (key. keycode == W_KEY_HELP) 
hel pSubSystem(QUERY_HELP ,QUERY_HELP_INDEX); 


where the two parameters are the resource IDs of the "top-level" Help resource and the "index" set of 
Help resources respectively. 


Like uPresentMenus and uRunDialog, hHelpSubSystem only returns when the entire Help operation has been 
completed by the user. It does its own error handling internally, automatically presenting suitable error 
messages where necessary. 


Help Resources 


As stated above, the function hHelpsubSystem requires two resource IDs as parameters. These reference 
instances of the HELP_ARRAY resource (defined in the Hwif reference documentation). These, in turn, may 
reference instances of STRING, TOPIC_ARRAY and yet other HELP_ARRAY resources, forming a potentially 
complex hierarchy. 


Note that in the definition of the resource struct HELP_ARRAY, the three fields must always appear in the 
given order. When declaring an instance of the resource struct HELP_ARRAY, the fields can come in any 

order (true for any resources declared in a resource file). The reason that it may be natural to rearrange 
the fields is that the displayed form of a general Help screen is: 


1. “topic” text in the title line (in bold). 
2. Any STRINGs in "strlst" come next. 
3. any associated topics defined by "topic_id" come last (bulleted and in bold). 


The "topic_id” field of any HELP_ARRAY resource, if present, must always reference an instance of the 
TOPIC_ARRAY struct (defined in the Hwif reference documentation). In turn the values in "id_tst” refer to 
further HELP_ARRAYS as shown schematically below: 


HELP_ARRAY (topic_id)--> TOPIC_ARRAY 


(id_lst item)--> HELP_ARRAY ... etc 


| 


Cid_lst item)--> HELP_ARRAY ... etc 


For example, the following definition has resource ID world_basics and contains all three possible types 
of field - "topic", "strist” and "topic_id”. 


RESOURCE HELP_ARRAY world_basics 


€ 

topic = "World"; 

strist = 
€ 
STRING {str = "To find a city, type first few letters";} 
STRING {str = "25 
STRING {str = "World button locks to cities in one country";> 
> 

topic_id = world_extra; 

> 


However, it is more common to omit at least one of these fields in any one HELP_ARRAY. 


16 


1 INTRODUCTION TO HWIF 
_ ee eeeeeeeSeSSSeSSSSSSSSSSSSSMMMSMMSSSSSSSSsssesesessseeee 


In practice, Help hierarchies tend to be built from what can be described as "top-level" variations of 
HELP_ARRAYS, where the "strtst” fields are omitted, and "bottom-level" variations, where the “topic_id" 
fields are omitted 


The “top-level” variations result in the presentation of bold, bulleted lists of further topics on which help 
can be had. For example: 


RESOURCE HELP_ARRAY query_help 
€ 
topic = "Query"; 
topic_id = query_help data; 
} 


The "bottom-level" variations result in the presentation of UNbold, UNbulleted text Strings. For 
example: 


RESOURCE TOPIC_ARRAY query_help data 

€ 

id_lst = 
€ 
query_list, 
query_func, 
query_memories, 
query_percent, 
query_tips 
7 

> 


In turn, query_memories, for example, is the ID of the resource: 


RESOURCE HELP_ARRAY query_memories 


{ 

topic = "Memories MO-M9"; 

strlst = 
{ 
STRING {str = "Top line shows current memory";}, 
STRING {str = "'M In’, 'M+" ete work on it":}, 
STRING {str = ";}, 
STRING {str = "Change or set with 'Change memories'":} 
3 

> 


Thus, calling hHelpsubsystem with first parameter query_HELP displays QUERY in the title line of the Help 
display followed by a list of bold ,bulleted items of which MEMORIES MO-M9 is the third. Selecting 
this item from the Help menu causes the above text to be displayed. 


Note that neither the system code at run-time, nor the resource compiler offers any assistance in word- 
wrapping strings of Help text. The developer must ensure that individual lines do not become so wide 
that a run time "Too wide" error message is given. 


The HELP_ARRAY identified by the index resource ID parameter to hHelpsubSystem (ie the second parameter) 
is only referenced when the user requests an "Index" of all available Help topics, either by pressing 
CONTROL +HELP inside the Help subsystem or by selecting the "Index" item and pressing ENTER. Either 
way, the system code constructs a sorted alphabetical list of topics from two sources: 


1. the help index at resource 112 in the "system" resource file (which the system code 
automatically opens on behalf of all Hwif applications). 


2. the HELP_ARRAY resource referenced by the caller's index resource ID parameter(if non-zero). 


In constructing the list, the system code ignores any "strist” and "topic" fields in the top-level 
resource. 


A measure of context sensitive help can be achieved by allowing the values passed to hHelpsubSystem to 
depend on the program state. 


Finally, whenever a Help screen is constructed, the system code automatically appends a reference to 
"Help on help" and "Index" provided that these are not already provided by the developer. 


17 


PROGRAMMING IN HWIF 


Help dialogs (an older alternative) 


In previous versions of Hwif, it was not possible to access the built-in Help subsystem from an Hwif 
application. However, two utility routines were available (and still are!) in the Hwif library that allowed 
the presentation of a broadly comparable but possibly less satisfying Help dialog suite. 


The routine uDiatogMenu allows the presentation of a menu-like dialog broadly equivalent to the top level 
in a Help dialog suite. Typically, an application would launch this dialog when the HELP key is pressed. 
The application supplies a range of topic titles, and the user cursors up and down in standard manner to 
select a topic of further interest. The value returned from udiatogMenu informs the application which 
topic was selected. 


The application can then make use of the routine uDisplayText to present a dialog containing up to seven 
lines of additional textual information (for the body of the help topic selected). When this is exited, the 
application can, if desired, present the top-level dialog again. 


Ee ee es ree a ee ee ee ee 
Line editors and multi-line editors 


In addition to the various editors that are available as items in dialogs, the Hwif library also allows access 
to single- and multi-line editors, outside of the context of dialogs. 


Such editors allow the user to alter data, without having to invoke a dialog box for this purpose. 
Advantages include: 


= compared to a corresponding (scrolling) editor in a dialog, a multi-line editor allows the user to 
see more text at one time, and the text can contain embedded carriage returns 


= any Find text (for example) can remain permanently visible on the screen, in its own edit box, 
instead of being visible only when the user requests the presentation of a Find Dialog 


= if the entries in for example a Diary are laid out on screen, the user can edit them in place, 
rather than having to use a dialog and lose sight of the overall screen layout in the meantime. 


Invoking these editors via Hwif library calls does not significantly add to the total size of the code of an 
application, since the Hwif library calls merely provide access to editing functionality that is already 
present in the Series 3 ROM. 

Editing features supported 

Hwif editors allow applications to make use of the following features: 


= in multi-line editors, text is automatically word-wrapped at the visible right margin specified; 
users can start new paragraphs by pressing the ENTER key 


= in multi-line editors, the display automatically scrolls vertically when required 
= the user can show or hide paragraph ends and spaces 


= the text can be displayed bold, italicised, underlined, monospaced, and/or double height (though 
the font style cannot vary from word to word inside any one editor) 


= the width of an editor can be changed dynamically on request, for example if the user hides or 
shows a permanent status window 


"it is possible to display a side cursor in the left margin; the width of the main (flashing) cursor 
can also be controlled (and can be set to zero) 


= the functions cut, copy, paste, evaluate, find, and replace are all available. 


Presenting an editor 
The minimum steps required to include an editor in an application are the following: 


= create the editor, using the cal! heBopen 
= when appropriate, emphasise it, using the call hEBEmphasise, so that it displays a flashing cursor 
= from time to time, pass it relevant incoming keypresses, using the call hEBHandleKey 


= when the application needs to know the current contents of the editor, the call hEBSenseText can 
be made. 


18 


1 INTRODUCTION TO HWIF 


The call heBopen passes a filled-in _ED1IT_Box struct, to customise the editor. This specifies (among other 
things) the position of the top left of the editor on the screen, the maximum number of characters that the 
editor is to accept, the width and height of the editor, the font style to use, and the spacing between lines. 


For example, the following code fragment creates an editor four lines deep, of width 12 pixels less than 
the current screen width, and which supports clipboard functionality and a left cursor: 


H_EDIT_BOX heb; 


heb. win=MainwWid; /* ID of main window */ 

heb.maxchars=255; 

heb. vulen=ScreenWidth-12; 

heb.visl ines=4; 

heb.pos.x=5; 

heb. pos. y=36; 
ebH=hEBOpen(H_EDIT_BOX_VISLINES|H_EDIT_BOX_LEFT_CURSOR|H_EDIT_BOX_CLIPBOARD,&heb); 


The return value ebi (the handle of the edit box) should be used to identify this particular editor (as 
opposed to others) in subsequent hEBxxx calls. The call returns zero if there was insufficient memory to 
create the editor. 


In the above example, the variable screenwidth has previously been set up by the application to the 
current width of the screen (this can vary depending on whether a permanent status window is visible). 
The variable Mainwid is the ID of the main screen window, as discussed below in connection with the 
Console. 


The call hEB0pen creates an editor without any text in it. Text can be placed into the editor in a variety of 
ways, chief amongst them the call 


hEBSetText(VOID *ebH, TEXT *pb, INT blen) 


In using the calls hEBSetText and hEBSenseText, it is important to understand that Hwif editors store text 
internally in one contiguous buffer. The buffer itself is always terminated by a zero. Paragraph ends are 
recorded using the character 13 (\n). Thus text set into an editor should not contain any zero (except, 
possibly, after blen characters - in which case it is harmless). For example, 


hEBSetText(ebi,"Hello world\nThis is Hwif",24); 


The call heBSenseText simply returns a TEXT* pointer to the editor's own copy of the text being edited. 
Clearly, this has to be treated with care. A copy of the text may have to be made (using, for example, 
p_scpy) before closing down the editor or otherwise changing its contents. 


As well as the textual content of an editor initially being zero, the cursor position also starts off at zero; 
likewise there is, by default, no select region. These settings can be overridden by means of the call 
hEBSetSelect. If required, there is also a corresponding call hEBSenseSelect. 


Word wrapping can, effectively, be turned off by using the heBsetMargin function. 

For more details of these and other editing calls, see the reference section, or the worked example 
applications. 

Applications with more than one editor 

Applications with more than one editor can present an impressive appearance to users. 


What the user sees is two or more regions of the screen - such as the Find Window and the Record 
Window in a database application - each enclosed in its own border, and each housing an editor. The user 
typically navigates between these windows using the TAB key to switch focus, and editing keys are 
directed to whichever editor currently has the focus. 


To implement such a set up, an Hwif programmer needs to take the following steps: 


= the sizes and positions of the various screen components have to be carefully calculated (see the 
example applications for some guidance on this) 


= the requisite number of editors have to be created, and their handles stored 
= a variable has to be dedicated to recording which of the editors currently has the focus 


= for each editor, a graphics call such as gBorderRect has to be made, to produce an appropriately 
shadowed boundary (with a heavier shadow for the editor possessing the focus). 


19 


PROGRAMMING IN HWIF 


On receipt of the TAB key, the application switches focus, by means of two calls to gBorderRect (one to 
remove the shadow from the editor that is losing the focus, and one to apply a shadow to the editor that 
is gaining it), and two calls to hEBEmphasise. The effect of these latter calls is to control whether a 
flashing cursor is displayed, and whether a select region is highlighted. 


Thus part of the code in the MainLoop of an application with three editors could be as follows: 


switch (key. keycode) 
€ 


case W_KEY_TAB: 
RotateFocus(key.modifiers&W_SHIFT_MODIFIER); /* forwards or backwards */ 
break; 
case W_KEY_RETURN: 
if Cemph!=2) 
break; /* else fall through */ 
default: 
hEBHandleKey(edit [emph] , key. keycode, key.modi fiers); 
} 


In this application, the handles of the three edit boxes are stored in edit [0] through edit (2), the variable 
emph records which currently has the emphasis, and of the three editors, only the third is multi-line (hence 
the test in the W_KEY_RETURN branch). The effect of SHIFT+TAB is to rotate the focus in the opposite 
direction to plain TAB. 


Edit boxes and saved file versions 


One important difference between restricting users to edit data via dialogs, and allowing them to edit the 
data in place, in single- or multi-line editors, is that menu commands can arrive at any time in the middle 
of editing in the second case (but not in the first). The point is that access to the menu bar is impossible 
while a dialog is in place; not so when text in an edit box is being edited. 


Applications which use edit boxes may therefore have to check, before carrying out any menu command, 
that their own record of the contents of the edit boxes is up to date. 


To this end, the routine hEBSenseChanged exists, which reports whether or not an edit box has had its 
contents changed. There is also a routine hEBClearChanged, which clears the internal "changed" flag 
maintained by the edit box. 


When file-based data is being edited via a dialog, an application can often choose to write any changes to 
file, immediately the dialog completes (especially in the case of a database application). But when data is 
being edited via an edit box, the application obviously cannot write out any changes every time a keypress 
is received. This leads to there being a potential difference between the file version of some data, and the 
current in-memory version. It is up to applications to keep careful track of this difference, and to decide 
when changes should be committed to file. 


ESS en ee ee ee ne 
Printing 
Hwif library routines provide support for applications: 


# invoking the standard Print Setup dialog suite (specifying page size, margins, headers and 
footers, and so on) 


= actually printing. 


Printing features supported 


Any printing from an Hwif application automatically conforms to the parameters specified by the user via 
the Print Setup dialog, and is automatically directed to the file or device specified by the user in the 
Printer Setup dialog in the System Screen. As the printing takes place, the standard Printing dialog is 
presented, informing the user of the page currently being printed, and containing a Cancel button to 
allow the printing to be abandoned. 


Rom resident code performs the pagination and any word-wrap required, and ensures that any headers 
and footers specified by the user are appropriately positioned. That is, including Hwif printing library 
calls in an application does not significantly add to the total size of the code of an application, since these 
library calls merely provide access to functionality that is present in the Series 3 ROM. 


20 


1 INTRODUCTION TO HWIF 


All that an application needs to provide is a callback function which provides the data for each new line 

(or series of lines, if word-wrap is required) to be printed. 

The Print Setup dialog 

In order to enter the Print Setup dialog suite, an application only needs to include the single line of code 
hPrintSetupDialog¢); 

There is no requirement for the application to record the values chosen by the user in this dialog suite, as 

ROM resident code does this automatically. 

The basic mechanism of printing 


As an example of how to print, consider an application which stores its data as a series of records 
accessed via an index, table[]. These records in turn consist of a date and time, and a text string, all 
defined by the struct 


typedef struct 


{ 

ULONG sdate; /* time and date */ 

UWORD tlen; /* length of text string */ 
TEXT “pb; /* address of text string */ 
> ENTRY; 


The following two routines suffice to print the application's data: 


LOCAL_D UWORD PrintRec; /* entry currently being printed */ 
LOCAL_D UWORD PrintState; /* which PART of the entry is currently being printed */ 
LOCAL_D TEXT PrintBuf{[H_TIME_LN_DATE_STRING] ; 


LOCAL_C INT PrintLineCH_PRINT *pr) 
{ 
ENTRY *pent; 


pent=(&table[PrintRec] ); 
switch (PrintStatet+) 


€ 

case 0: 
pr->blen=SdateToBuf (&PrintBuf [0] ,&pent->sdate); 
pr->buf=(&PrintBuf [0] ); 
pr->style|=H_PRINT_STY_BOLD; 
break; 

case 1: 
pr->buf=pent->pb; 
pr->blen=pent->tlen; 


break; 

case 2: 
pr->blen=0; 
PrintState=0; 
PrintRec++; 
return(PrintRec! =count); 
> 

pr->flags|=H_PRINT_KEEP; 

return( TRUE); 

> 

LOCAL_C VOID PrintALL(VOID) 

€ 

if (!count) 
{ 
winfoMsg("Nothing to print"); 
return; 
> 


PrintRec=0; 
PrintState=0; 
hPrint(PrintLine); 
d 


21 


PROGRAMMING IN HWIF 
Ss eS 


Of these two routines, the latter (Printal) is the controlling one. The general form of this routine is 
actually as follows: 


CollectPrintDetails(); 

Check! fReal lyNeedToPrint(); 
InitialisePrintStateVariables(); 
hPrint(PrintLine); 
PostPrintMessage(); 


In all cases, the centre-piece of a PrintAlt routine is the line 
hPrint(PrintLine); 


or equivalent. When the flow of program execution reaches this line, it remains inside the hprint routine 
until printing has completed (either naturally, or on account of the user terminating, or on account of 
some kind of error). Thus any following line of code, such as a call to PostPrintMessage, is executed only 
after printing is complete. In this sense, hPrint is similar to the calls uRunDialog and uPresentMenus; a lot 
can take place on the screen without the program progressing any further through application code. 
However, in one crucial respect, hPrint differs from these other calls; the application-supplied callback 
function PrintLine is repeatedly visited from inside the hprint call. 


The PrintLine callback function 


Each time system code calls the PrintLine function, the application has to supply the data for the next 
line (or series of lines) to print. 


It is the responsibility of the application to keep an independent record of how far the printing has 
progressed - so that the appropriate data can be passed each time. This record - the so-called Print state 
variables - has to be in some static data (or in an alloc cell whose handle is a static variable). In the 
above example, the variables printstate and PrintRec play this role: printRec counts through the records 
(from 0 through count-1), and PrintState records which part of each record is currently being printed: 


® the text corresponding to the date and time” 
= the main text of the record 
= a possible blank line underneath the record, to separate it from the following one. 


Each time PrintLine is called, the application has to fill in parts of a passed H_PRINT struct. The parts that 
an application is most likely to want to write to are: 


blen the length of the text for the line (or group of lines) 

buf the address of a buffer containing the text to print 

style possible further embellishment of the font style selected by the user in any 
Print Setup dialog 

flags the application may wish to or in the bit }_PRINT_KEEP, to request the system 


software to keep this line (or group of lines) together on the same page with 
the following line, if possible; another potentially useful flag is H_PRINT_PAGE, 
to force the emission of a form feed before the line is printed. 


Additionally, the return value from PrintLine has the following significance: 
= the application should return FALSE when it has no more data to print 


= otherwise, the application should return TRUE. 


The location of the print buffer 


Note that pr->buf must point to a buffer that will continue to exist after the routine printLine has 
returned. 


In the above example, the main body of text can be printed simply by setting pr->buf equal to the 
application's own pointer to where this text is stored. However, the text corresponding to the time and 
date is another matter, since the application evidently only stores this in the form of a ULONG. The 
application routine SdateToBuf converts the time and date from a ULONG into text form (presumably using 
the Hwif time-text utility functions, discussed later). However, it would be a severe error to declare a 
buffer to hold this textual representation as an automatic on the stack inside printLine. 


22 


1 INTRODUCTION TO HWIF 


The Print Details dialog 


Although the above example lacks such a dialog, it is possible for an application to present its own dialog 
prior to proceeding with a print. 


The purpose of this dialog - if not only to confirm that the user wishes printing to go ahead - could be to 
collect additional parameters affecting the way the printing is done. For example, the user could be asked 
to choose whether all records should be printed, or only a selected set (say only those records which are 

somehow tagged). Again, the dialog could control whether each new record should begin on its own new 


page. 


Note that any such Print Details dialog differs in function from the Print Setup dialog available via 
hPrintSetupDialog: 


= the Print Setup dialog is common to all applications, whereas Print Details dialogs differ from 
application to application 


™ — system code takes care of recording and implementing the choices made from the Print Setup 
dialog, but it is up to applications to record and implement choices made in any Print Details 
dialog. 


Word wrapping during printing 


In most cases, applications have no need to be aware of any word-wrapping that may take place during 
printing. Whether or not some text is printed on one line or over more than one line depends, after all, 
on choices made by the user in the Print Setup dialog - such as the page size, the left and right margins, 
and the default printing font. In all cases, the ROM printing code automatically ensures that the specified 
margins are respected, with excess text being placed on the following line. 


Occasionally, however, an application may wish to specially indent subsequent lines of a wrapped 
paragraph. This can be achieved by using the Hwif library call hprintSetsi - which sets the Subsequent 
Indent for any wrapped lines. 


At the same time, amy line can be arbitrarily indented, by means of writing to the indent field of the 
passed H_PRINT struct. 


The units any such indents are expressed in vary considerably from printer to printer. To guide the 
application as to suitable values, two additional calls are available: 


hPrintSenseBufWidth returns the width, in printer units, of a specified buffer (when 
printed in the selected default printer font) 


hPrintSensePageWidth returns the width, in printer units, of the paper being printed on 
(minus its margins). 


Printing the contents of multi-line editors 
Recall that Hwif multi-line editors use the character 13 (\n) to record paragraph ends. 


Accordingly, if data produced in multi-line edit boxes is to be printed, it should first be scanned for 
embedded \n's, along the following lines: 


case 2: 
PrintBuf=index (PrintRec] .note.buf; 
PrintLen=index [PrintRec] .note. len; 
default: 
pr->buf=PrintBuf; 
ind=p_bloc(PrintBuf ,PrintLen, '\n'); 
if Cind>=0) 
€ 
pr->blen=ind; 
PrintLen-=ind+1; 
PrintBuf+=ind+1; 
> 
else 
€ 
pr->blen=PrintLen; 
PrintState=0; 
PrintRec++; 
> 


23 


PROGRAMMING IN HWIF 


SS SSS een Oe ee ee ee eee ee 
Time-text utility functions 


The Hwif time-text functions provide a convenient way of generating textual representations of times 
and/or dates, that reflect the user's preferences as given in the Formats dialog in the Time application. 


In general, a textual representation of time and/or date consists of a combination of some of the following 
components: 


= numerical representations of the day in the month, the month in the year, the year, and the 
century 


® numerical representations of the hour, the minute, and the second 


= the name of the day in the week, and the name of the month in the year - each of which may be 
abbreviated 


= a suffix (such as ¢h or rd) after the day number in the month 
® an am or pm indicator 
= time and date separators (such as colons and slashes). 
The htTxxx functions provide textual representations as combinations of the above, taking data from: 


* atime and/or date that has previously been specified, using the htTSetTime call (which accepts 
any of the P_DAYSEC, P_DATE, or system-time representations) 


= formatting preferences previously indicated by the application, using the calls hTTsetformat and, 
possibly, htTSetAbbreviations 


= the user's current preference for whether time should be am/pm or 24 hour, for whether the 
month should come before or after the day, and for what the date and time separators should be. 


The resultant text itself is obtained by a call to hTTSenseText. 


Each of the above calls has to pass a handle that has previously been obtained by a call to htTopen. This 
allocates resources that the subsequent calls access. When these resources are no longer needed, they can 
be freed by a call to htTClose. 

Default textual representation 

In the absence of a call from hTTSetFormat: 


= any time string generated consists of hours, minutes, and seconds (all expressed numerically), 
together with the current time separators 


= any date string generated consists of the day in the month, the month in the year, the year, and 
the century (again, all expressed numerically), together with the current date separators. 


In the absence of a call from hTTSetAbbreviations, any day and month names generated are given in full, 
without being abbreviated. 
Refreshing the format on returning to foreground 


Ideally, every time an application making long-term use of the time-text functions is brought into 
foreground, it ought to reset the format of any time-text channels it currently has open. This ensures that 
any changes made by the user in the meantime (when the application was in background) are picked up. 


For example, part of the Maintoop of an application displaying time in textual form might be 


if (key. keycode&W_EVENT_KEY) 


€ 

if (key. keycode==CONS_EVENT_FOREGROUND) 
€ 
UpdateT imeFormat(); 
Display(); 
> 

> 

else 


24 


1 INTRODUCTION TO HWIF 
—_— EEE 


If the application displays time without any seconds, and displays the month name, the day name, and a 
suffix after the day number, the contents of UpdateTimeFormat could be simply 


LOCAL_C VOID UpdateTimeFormat(VOID) 
€ 
hTTSetFormat(ttH,H_TIME_FORMAT_NO_SECONDS|H_TIME_FORMAT_SUFFIX | 
H_TIME_FORMAT_DAY_NAME|H_TIME_FORMAT_MONTH_NAME); 
> 


This call re-asserts the application's requirements, but allows the user's latest preferences on matters of 
time and date separator, 12 or 24 hour clock, and month coming before or after date, to be picked up 
too. 


Eee ne ee ee ee ee eee Sy 
Date/Time-text utility functions 


These are a small set of functions which construct and manipulate stand-alone date/time text editors. As 
such, they need not exist within a dialog. In some respects they are similar to the time-text functions but 
have fewer date/time formats. 


The textual representation of date is limited to the ten characters DD/MM/YYYY. For example: 
20/07/1993. 


The textual representation of time can be in any of the following formats: 


=" HH:MM:SS representing either a time duration or a time of day in hours, minutes and seconds. 
If it represents a time of day then, depending on system settings, the hours can be in either the 
24-hour or the 12-hour format. If the latter, the string will be followed by the characters am or 
pm. 


= HH:MM representing either a time duration or a time of day in hours and minutes. If it 
represents a time of day then, depending on system settings, the hours can be in either the 24- 
hour or the 12-hour format. If the latter, the string will be followed by the characters am or pm. 


The formatting can be done either at creation time in a call to hDTOpen or when setting a value in a call to 
hDTSet. 


The value and formatting information can be retrieved by a call to hoTSense. 
A call to hDTSel fCheck performs a validation on the value currently held. 


A call to hDTHandleKey handles the current keypress, assuming that there is suitable code which can 
capture keypress events. 


A call to hDTEmphasise can switch the emphasis for the current date/time editor. In other words, 
depending on whether the second parameter is set to TRUE or FALSE, text highlighting can be turned on or 
off. The emphasis can also be set at creation, i.e. on a call to hOTOpen. 


Each of the above calls must pass a handle that is obtained by the call to hnTopen. This allocates resources 
that the subsequent calls access. When these resources are no longer needed, they can be freed by a call 
to hDTClose. 


It is worth a reminder that the position and width of the text editor on the screen can also be specified at 
creation, i.e on a call to hoTOpen. 


SSS SS ae ae) 
Hwif, the Console, and screen output 
The visual output of an Hwif application generally consists of: 

= intermittently, menus and dialogs 

= more permanently, single- or multi-line editors 

= atemporary or permanent status window 

= additional graphics and text effects. 


These additional graphics and text effects are achieved by a mixture of Wlib calls such as winfoMsg, 
gPrintBoxText, gBorder, ginvObloid, wsEnable, gClrRect, wScroltRect, and so on. 


25 


PROGRAMMING IN HWIF 
ee Ss eee 


In comparing these graphics calls with simpler console routines such as p_puts, three possible difficulties 
emerge: 


= there is a significant learning curve of new functions and new function names 


= console routines, being text-based, automatically take care of positioning the cursor, whereas 
more painstaking calculation is, inevitably, required with graphics functions 


= before making any of the graphics calls, a significant amount of initialisation has to be 
undertaken, involving connecting to the Window Server as well as creating windows and 
graphics contexts when required. 


Three options for graphics output 
In general, there are essentially three options for application screen output: 
= — stick strictly to console services such as p_printf and p_puts 


# use the routines in the Window Server library, but using windows with backed-up bitmaps, to 
avoid the need to undertake the conceptually more taxing burden of do-it-yourself window 
redrawing 


= embrace the full philosophy of window redrawing. 


Hwif applications fall decidedly into the second of these three options. Routines such as p_printf produce 
an output that is clearly inferior, in graphics quality, to that of the menus, dialogs, and editors in the 
Hwif arsenal. 


The initialisation routine uCommontnit hides the complications of actually connecting to the Window 
Server - thus alleviating one of the possible disadvantages mentioned earlier, as regards using Window 
Server calls. Once the call ucommoninit is complete, the connection to the Window Server has already 
been established (there is no need for any independent call such as wStartup), and a backed-up window 
the size of the screen has been created. Similarly, the routines uGetkey and uGetKeyA hide the 
complications of receiving and decoding events from the Window Server. 


At the same time, it is just as simple to invoke graphics routines such as winfoMsg (which produces an 
information message at the bottom right commer of the screen) and wSetBusyMsg (which produces a flashing 
busy message) as it is to invoke p printf and p_puts - provided, that is, that you have learned of the 
existence (and the names) of these graphics routines. 


On the other hand, there is no requirement to go the full extent of adopting the window redraw 
mechanism that ultimately gives the best performance from the window server. Just as console i/o is 
inappropriately primitive, compared to the i/o of Hwif dialogs, edit boxes, and menus, the full redraw 
mechanism is inappropriately advanced. 


Indeed, the three stages of graphics output listed above correlate closely with the three general stages 
through which programmers can develop, in acquiring fuller mastery of the Series 3 software: 


= use only of console routines is appropriate to an initial encounter with the Series 3, and for 
applications whose user interface is unimportant 


= use of the graphics calls (but not of the intricacies of doing your own redraws) matches 
applications at the Hwif level of sophistication 


= application programmers for whom high performance is vital must adopt not only the concept of 
redrawing windows but also the Psion proprietary object-oriented development system. 


Practical acquisition of graphics techniques 


As far as acquiring familiarity with the varied Window Server library calls is concerned, it is intended 
that the accompanying Hwif example applications will prove useful in this regard (as well as in regard to 
illustrating the Hwif library calls). These provide sufficient illustrations for would-be graphics 
programmers to develop enough confidence to subsequently branch out, by themselves, into the wider 
reaches of the Window Server reference manual. 


Two of the main types of graphics displays on the Series 3 are amply treated within these examples: a 
series of edit boxes (each with their own border), and a scrolling vertical list. Hiding and showing 
permanent status windows is also covered more than once. 


26 


1 INTRODUCTION TO HWIF 
—_—_ Eee 


Hwif opens the console channel 


As a matter of implementation, the call ucommontnit which prepares the ground for all subsequent Hwif 
calls itself involves opening the console device (if it is not already open). As is standard, the handle of 
the console control block is written into the static winHandle. 


Almost all of the time, Hwif programmers can be oblivious of this implementation decision, and need 
make no reference to wintandle. The supplied routines uGetKey, uGetkeyA, and uKeyPressOutstanding hide 
some of the details of the interaction with the console. 


However, any console program can, by default, be terminated at any time by the user simply pressing 
PSION + ESC. In case this is undesirable, an application should make the call 


uEscape( FALSE); 
during its SpecificInit routine. 


An important point to note is that if the global variable Useful screen is zero on entry to the call to 
uCommoninit, the console device is initialised in compatibility mode; in other words, the behaviour and 
appearance of the console on the Series 3a or the Workabout emulates that of the console on the Series 3. 
The main window 


At the end of the call to uCommoninit, a graphics window exists, the size of the whole screen - 240 by 80 
pixels on the Series 3, 480 by 160 pixels on the Series 3a and 240 by 100 pixels on the Workabout (in 
full Series 3 compatibility mode on the Workabour there is a gap of 10 pixels both above and below the 
window). 


In practice, it is rarely necessary for Hwif programs explicitly to create any additional windows. 


Many graphics calls need to know the window ID of the window they are to operate upon. The Hwif 
routine uF indMainWid returns this ID. 


Typically, Hwif applications include a line such as 
MainWid=uF indMainWid¢); 


early in their SpecificInit routines. 


Grey 


On the Series 3a and the Workabour, drawing can be done not only in black & white but also in grey. To 
draw grey in the main console window, it must first be enabled. 


This is done by calling the function uEnableGrey which is more fully described in the Hwif Reference 
Documentation chapter. 


Ideally, if grey is to be used, it should be enabled early in the life of the application, preferably during 
the initialisation phase. On no account should grey be used before being enabled - the application is liable 
to fail. 


Note that utnableGrey changes the ID of the main console window. Therefore, ensure that uFindMainwid is 
called to fetch the new ID. 


SS ee a ee ee 
Dual mode applications 


In general, applications which are intended to run on the Series 3 as well as on the Series 3a and/or 
Workabout often need to be able to distinguish the type of machine on which they are Tunning. 


For example, grey is available on the Series 3a and Workabout but not on the Series 3. Applications that 
are to run on more than one type of machine may wish to make use of such additional features if they are 
available. 


Global variables such as _UseFul \screen can be used to help an application differentiate between the 
different situations. This is discussed in more detail in the description of the function ucommontnit in the 
Hwif Reference Documentation chapter. 


One point that needs to be discussed here though is the question of application icons, since the form of 
icon required for the Series 3 is different from that needed for the Series 3a and Workabout. For 


27 


PROGRAMMING IN HWIF 


applications that are intended to run on the Series 3 and either or both of the other machines, a .pic file 
can be created to hold two separate icons. 


In this situation, the .pic file will first contain a bitmap, 24 pixels wide by 24 pixels deep, for the 
Series 3 icon. This is immediately followed by two bitmaps, each 48 pixels wide by 48 pixels deep, for 
the Series 3a/Workabout icon. This is discussed more fully in the Series 3 Programming Overview 
chapter in the Series 3/3a Programming Guide. 


In order to ensure that the appropriate icon is used, the function hcrackCommandl ine must be called before 
the call to uCommoninit. This must be done even though the application might have no interest in the 
contents of the command line. 


Thus, the general shape of the main¢) of an application might look like this: 


GLDEF_C VOID main(VOID) 
€ 
_UseFullScreen = TRUE; 
hCrackCommandL ine(); 
uCommonI nit); 
SpecificInit(); 
MainLoop(); 
} 


This ensures that all the relevant Epoc statics are correctly initialised by the time the application connects 
to the window server (ie inside ucommontnit); this is when the window server decides which icon to use 
for the application. 


If an application is also interested in the value returned by hCrackCommandt ine, then it should save the 
returned value at this point. It must NOT call hcrackCommandL ine a second time. 


EE ee ee a ae] 
Storing data to file 


Applications which manipulate significant quantities of data will in general wish to allow users to save 
this data to file. Such programs need to address the following points: 


= the user interface allowing the user to specify which file(s) to open, save, merge, ... 
= the format of the data, as saved on file 
= the mechanism of reading/writing the data to and from file 


= possible special requirements of writing files in a manner that makes best use of the Flash 
storage medium 


= keeping the System Screen and the status window informed as to which file is currently being 
used. 


The final topic is discussed in the section following this one. On the question of user interface, Hwif 
dialogs allow the inclusion of either of two types of filename specifier: 


= filename selectors constrain the user to select a file that already exists - as is appropriate for 
commands such as Open and Merge 


= filename editors allow the user to type in the name of a file that may or may not already exist - 
as is appropriate for commands such as Save as and New. 


In either case, users can bring up the full filelist, simply by pressing TAB. Or they can press 
CONTROL+TAB for ease of swifter navigation to more distant files. Again, in either case, the dialog 
supplies an associated disk selector, without the programmer having to explicitly arrange for this. If the 
user has enabled Remote Link, drives on REM:: automatically become available for selection, alongside the 
local ones. All this happens just by virtue of a filename selector or editor being added to a dialog, being 
taken care of by ROM resident code on behalf of the Hwif programmer. 


Given also that there is a wide range of flags allowing further customisation of the exact behaviour of 
these dialog items, Hwif programmers should find all their needs amply catered for, as regards the user 
interface of choosing files. 


28 


1 INTRODUCTION TO HWIF 
eS eee 


The Dbf file format 


The Plib file i/o functions can be used for any variety of data formats on file, and Hwif programmers can 
choose whatever they feel most comfortable with. (There is some special treatment for text files.) 


However, much can be said in favour of the Dbf file format that is used by, among other applications, 
the built-in Database and Agenda: 


® It is designed with Flash-friendliness as a high priority, with incremental file modification as 
individual records are updated 


= rom-resident code provides a rich set of services to simplify access to files of this format 
= services such as random and sequential access are both highly optimised 
= other services such as merging and compressing databases are easy to use. 


The Hwif library itself has very little to add to these Dbf services (there is a utility function, 
hIsDbfCompressible, to determine whether a given Dbf channel supports file compression). More 
important is the fact that the Hwif example applications illustrate clearly the use of these Dbf functions. 
Dialling telephone numbers 

Typical contents of databases include telephone numbers. 

The Hwif library includes a function, hoTMFstring, to emit DTMF tones corresponding to a passed string. 


This function uses the tone lengths and pauses as specified by the user in the World application (or 
otherwise), and reverts to system defaults in the absence of any such setting. 


= SSS SS Se ee ee et ee ee eee Ee 
Communication with the System Screen 


An important aspect of the Series 3 is the way all the built-in applications communicate with the System 
Screen: 


« This name of any file currently open is displayed in bold in the file list in the System Screen 
= This name is also displayed in any status window shown 


= On a request from the System Screen, an application can close itself down tidily, saving any 
changes to file as appropriate 


" Alternatively, applications can be requested to switch files, to change which file they are 
currently editing. 


Applications use two mechanisms to communicate to the Series 3 OS their preferences concerning file 
switching, as well as the name of the file they are currently editing: 


= some data is written at compile time into a shell data (shd) file that is linked into the 
application's .app file; this data includes the expected extension of any files to be edited, and the 
default top-level directory, as well as the more basic point of whether the application is file- 
based at all 


= other data can be written at run time to various reserved Epoc statics; these include the full path 
name of the file currently being edited. 


There are routines in the Hwif library to take care of keeping the various Epoc statics up to date. 
However, applications programmers may need to know about two of these statics directly: 


UWORD DatLocked this should be set to TRUE whenever, over a potentially extended 
period, the application is unable to respond to a Switchfile or 
Shutdown message from the System Screen 


TEXT *DatUsedPathNamePtr this points to a buffer giving the full path name of the file 
currently being edited. 


For full details on the interaction between Series 3 applications and the System Screen, see the chapter 
Communicating with the System Screen in the Series 3/3a Programming Guide. 


29 


PROGRAMMING IN HWIF 


Reading the command line 


In addition to being able to respond to requests of the Switchfile or Shutdown varieties, file-based 
applications on the Series 3 should also be able to read the command line they are sent when they start. 
This has a special form which can, however, be interpreted by means of the Hwif call hcrackCommandL ine. 


File-based applications would ordinarily include a call to hcrackConmandL ine as part of their initialisation. 
One of the consequences of this call is that the Epoc static patUsedPathNamePtr is initially pointed to an 
appropriate zero-terminated string in the body of the command line. 


Storing the name of the file currently open 


Initially, the name of the open file is part of the command line. However, when this has to be changed - 
either as a result of an Open or Save as command inside the application, or in response to a Switchfile 
request from the System Screen - a new buffer has to be used for this purpose. (The command line buffer 
is sized to precisely the right length needed for the initial file.) 


Typically, file-based applications will maintain a permanent buffer, of length p_FNAMESIZE, to store any 
change in the name of the file open. Once the new name has been copied into this buffer, the call 
hSetUpStatusNames should be made, to adjust all Epoc statics as appropriate, including 
DatUsedPathNamePtr. 

The protocol of messages from the System Screen 


A keycode with value equal to cONS_EVENT_COMMAND means that the System Screen wishes to communicate 
with the application. In case there is any doubt as to what the message is, it can be determined by making 
a Call to wGetCommand. 


For more details, see Communicating with the System Screen in the Series 3/3a Programming Guide. 


SSS ESS a Sa ee a eee 
Some notes on run-time errors 
Errors arising from Hwif calls include the following types: 


=" programmer errors, such as making calls with unsuitable parameters (or disregarding earlier 
errors) - these may well result in the application being panicked 


= as a special case of programmer error, menus or dialogs may turn out too wide to display 
properly on the screen; this is generally indicated by a return value E_GEN_TOOWIDE from a call 
such as uRunDialog (there is also an associated error E_GEN_TOOMANY) and the user will see a Too 
wide error alert 


= shortage of memory in the application data space, generally indicated by a return value 
E_GEN_NOMEMORY 


# shortage of memory in the Window Server data space - also indicated by the same return value. 


A well-written application needs to be able to recover from an out-of-memory (OOM) error, without 
falling over in the process or corrupting or losing any data. In general, an application should always test 
the return value of Hwif calls, to see whether they have succeeded, or whether they have failed with 
OOM. 


However, there are some cases when an application can legitimately assume that a call always succeeds: 
= if the call is part of the initialisation of the application 


= and if the application has specified its start-up heap appropriately (this is done as a line in the 
.pr project file that orchestrates linking) 


= and if the call only uses resources in the data space of the application (as opposed to resources in 
the data space of the Window Server). 


Note that in no case can an application legitimately assume the success of a call which requires Window 
Server resources. 


Application writers can make use of the Spy application to discover how much heap an application 
requires in order to start. 


30 


1 INTRODUCTION TO HWIF 
See 


Some errors to consider 


Other errors which application writers may need to consider include: 
= running out of SSD space when writing to a file 
" — not being able to find a specified file (because the relevant SSD has been removed) 
# the SSD being removed part way through a file operation 


" (perhaps the least obvious) the failure of a remote link connection to another filing system - say 
because the user has shut the connection down since opening a file on that filing system. 


Strategies on handling errors 


Any error during application initialisation is generally fatal. The user should be informed of what has 
happened and the application terminated. 


For example: 


LOCAL_C VOID SpecificInit(VOID) 
{ 
INT command; 


MainWid=uF indMainwWid¢(); 

CreateGC(); 

command=hCrackCommandL ine); 

if (ObeySystemCommand( command, DatUsedPathNamePtr , &dH)) 
p_exit(0); 

ReduceScreenSize(); 

gBorder(W_BORD_CORNER_4); 

wsEnable(); 

DisplayStart(); = 

> 


In the above example, notifying the user of the error takes place inside the routine obeysystemConmand. In 
other cases, an application may wish to take advantage of the fact that if it calls p_exit with a negative 
parameter, the OS will automatically present a notifier on its behalf. The text in the notifier is the system 
error message corresponding to the parameter to p exit. 


Another straightforward case to handle is an error, such as OOM, while building up a dialog. In fact, all 
that needs to be done in this case is to follow the procedure given in several of the earlier examples: test 
the results of calls such as uOpenDialog and uAddDialogItem, and simply break out of the general stream of 
program flow when an error is detected. The user will see the error (presented by Hwif library code), can 
opt to free some memory by shutting other applications down, and then retry the command by invoking 
the same menu choice as before. 


A similar approach can often be taken for cases such as errors when writing to an SSD. Alternatively , 
applications may wish, in these cases, to provide their own retry loop, and may even wish to insist that 
users successfully conclude the loop before allowing them to continue. This is appropriate for file-based 
applications in which the file must always be kept up to date. 


Sometimes, indeed, all that an application can do, on detecting an error, is to notify the user accordingly, 
and then terminate the application. For example, if a user removes an SSD containing an open file, and 
an application tries to read data from this file, the following sequence of events will occur: 


= the OS will present its own notifier, requesting that the SSD be reinserted 
= this notifier has two options: Retry and Fail 
= if the user selects Fail, the OS returns the error E_FILE_ABORT to the application. 


In such a case, there is little an application can do, apart from terminating gracefully. 


Reverting to the previous file 


Ideally, a file-based application should aim, where possible, at being able to recover from failing to 
switch files (in response either to a menu command, or to a request from the System Screen) by means of 
reverting to the previously open file. 


Thus suppose an application currently has file name1 open, on file channel fcb1, and that the user requests 
that file name2 be opened instead. Suppose further that, for one reason or another, name2 cannot be loaded 


31 


PROGRAMMING IN HWIF 


into the application (it may be the wrong type of file, it may currently be locked by another application, 
or whatever). Then the user could see one of two things: 


= an error notifier is presented, and then the application terminates 
= an error notifier is presented, and then the application reverts to its previous state. 


Evidently, the latter is preferable. It allows the user the opportunity to make due amends (for example, 
closing down another application which has the file already open) and then retry. 


A simple approach to this end is to keep the first file open until the second file is successfully loaded. 
Only when this is complete are the resources associated with the first file freed. 


For this reason, a file-based application will often possess a routine ChangeOverTo (with parameters such 
as the new filename and its new control block) that has the role of finally closing down the previous file, 
and then altering the application's records of the name of the current file. See the example given earlier, 
in the section Typical dialog usage. 


Errors when formatting edit boxes 

Single- and multi-line edit boxes pose an additional type of problem, in handling OOM errors. 

In making a change to an edit box, OOM can occur in either of two ways: 
= there is insufficient memory to increase the contents of the edit box (eg to add another character) 
® the contents can be grown but the Jayourt cannot be recalculated. 


The point is that layout information (the location of all line breaks due to word-wrap, and so on) is 
dynamically allocated; consequently, the calculation of layout can fail. 


Paradoxically, it turns out that the best reaction an application can make to the second kind of error is 
usually to ignore it. Hwif library code will ensure that the user is informed of the shortage of memory, 
and the edit box will only be partially redrawn. Despite only being able to redraw itself partially, the edit 
box will not crash, and will hold on to all its contents in the meanwhile. Once additional memory 
becomes available, the edit box will recalculate its layout, and then redraw itself correctly. 


Sa ae aa a a ae eg ee 
Future developments 
In summary of the foregoing, it can be said that Hwif fulfils two separate roles with regard to 
applications programmers: 
= in its own right, it supports the development of a large variety of significant and potent 
applications, with an agreeable user interface 


= ina broader context, it serves as a critical stepping stone towards a full mastery of the Series 3 
API, including object orientation, low-ram window redraws, and the p_enter/ p_leave 
mechanism that removes most of the clutter from error handling. 


Whether programmers who learn Hwif will be content to stop there, and exploit the considerable avenues 
this opens up in its own right, or whether they will wish to press on in due course to master the full 
Series 3 API, will obviously vary from programmer to programmer. Both choices make good sense. 


32 


CHAPTER 2 


WORKED EXAMPLES IN HWIF 


SS a eee 
How to use the supplied examples 
The supplied example applications serve two purposes: 

= tutorial, with embedded suggestions for further exploration 

= reference pool, with varied illustrations of many parts of the Series 3 ROM software. 


In neither case is there any need for would-be Hwif programmers to examine the code for all of the 
example applications provided with this manual; nor is there any need to digest all the associated 
discussion this chapter contains. Instead, the expectation is that prospective applications developers will 
work through items in the tutorial that they find of interest, and will merely skim through the other parts. 


In this way, prospective applications developers will familiarise themselves with at least the general 
contents of the example applications. Then when they are planning their own application, they may well 
recall that one of the example applications does something similar (with a dialog, say) to something 
planned in their own application. In that case, the application developer can look up the relevant piece of 
source code, and consult the associated documentation, to discover how to produce the desired effect. 


Strangely enough, it is often going to be unhelpful for Hwif application writers to think that a certain 
feature of the user interface of, for example, the built-in Agenda application ought to be duplicated in 
their own application. For even were the relevant source code available for perusal, that code would 
almost certainly be laden with proprietary object oriented techniques to the extent of being well out of the 
grasp of the Hwif application writer. On the other hand, familiarity with the supplied Hwif example 
applications is likely to provide a much more appropriate set of models to follow. Quite probably, there 
will be something in one of these applications that does essentially the same job as in the desirable feature 
of the built-in application (albeit possibly not so elegantly). In contrast to the code of the built-in 
application, the code of the example Hwif application is suitable for being copied into the developer's 
own application. 


The embedded suggestions 


In all forms of learning, practice makes perfect. This is as true for programming in a new system (such as 
Hwif), as it is for learning in general. 


Accordingly, this chapter is regularly punctuated with "Suggestions" sections. These have been provided 
so that the would-be Hwif programmer who feels a bit unsure about some topic can have plenty scope for 
practising in that area. Trying out a few of the suggestions should increase understanding and boost 
confidence. Additionally, many of the suggestions provide a foretaste of topics to be discussed shortly 
afterwards. Others give hints on ideas that Hwif programmers might like to develop independently. 


Of course, readers are free to think up their own ideas for how to modify the various examples presented. 
However, especially in the earlier stages, ideas that seem perfectly straightforward extensions of the 
examples given might turn out to be significantly more involved than at first thought. Careful thought 
has been applied to the lists of suggestions given in the text, to avoid precisely this problem. 


Preview of the example applications 


The first example application, Query, focuses almost exclusively on the basic menu and dialog 
functionality of Hwif (use of the Hwif time-text utility functions is also illustrated). Examples are given 
of each of the possible items that can be included in dialogs. There are only a few graphics calls, and 


33 


PROGRAMMING IN HWIF 


these are completely straightforward. There is no file-handling (likewise in fact for all of the first four 
example applications). The application itself provides answers to questions that a user may wish to pose, 
such as the conversion of centigrade values into Fahrenheit or miles into kilometres, the next occurrence 
of a certain date combination (eg Friday the 13th), the size of a specified file, and the encryption and 
decryption (using a supplied key) of given text messages. 


The next example, Tables, introduces the important idea of reading a key asynchronously. This is 
because it involves the user typing in the answer to a multiplication problem (such as "4 times 9") before 
a timer that is ticking away on screen runs out completely. The application also introduces use of a 
simple Hwif edit box (with double height characters), and includes examples of several useful graphics 
techniques - with an animated action button and a growing gauge display outside of the context of a 
dialog. On exit, the state of the application is recorded in an environment variable, which is used to re- 
initialise the application the next time it is run. 


The application Remind functions as a sort of half-way house between the built-in Agenda application and 
the built-in Time application. It allows users to set alarms with text strings, which on expiry can, if 
desired, be "snoozed" by specified time intervals (but still allowing access to the remainder of the 

Series 3 in the meantime). Evidently, this application illustrates access to the alarm server device. As 
such, it demonstrates more of the important concepts concerning asynchronous i/o. Remind also 
introduces another large subject: printing and access to the Print Setup dialog. The main screen display is 
a scrolling list of all reminders scheduled by the user, automatically sorted into chronological order. The 
graphics calls employed demonstrate how to achieve smooth scrolling (without undue screen flicker). 
Finally, on receipt of the keypress CONTROL+MENU, Remind hides or shows a permanent status window, 
and adjusts the rest of its display accordingly. 


Notes is another application that functions as a half-way house between the functionality of two of the 
built-in applications: it straddles some of the characteristics of the built-in Database and Word 
applications. The screen is divided up into three edit windows - Title, Notes, and Find - with the data for 
a series of notes being directly edited in place (as opposed to being manipulated only through dialogs). 
As well as providing a wide-ranging survey of the editing facilities available via Hwif, the Notes 
application also gives a further example of printing. 


The next example, Dump, produces a Hex dump of a nominated file. This is displayed on the screen in 
the first instance, but a selected portion of the dump can be written out to a nominated file. The dump 
can be scrolled in any direction, and it is possible to search it, either for strings of text, or for byte 
streams. The application introduces file-handling, both of the file to be dumped (initially chosen from the 
System Screen), and of the file to receive a written record of the dump. Finally, the use of a special mode 
is illustrated, in which repeated cursor keypresses may cause the display to be drawn in its new position 
only when there is a suitable delay in receiving keys. 


Then comes a couple of applications illustrating different uses of the Dbf database subsystem. These 
applications differ from both Notes and Remind in that they make permanent copies of their data on file 
(whereas Notes and Remind just operate with in-memory data). Both of these applications deal with 
Shutdown and Switchfile messages from the System Screen. 


The first of these Dbf applications, Tele, is a simple example of a fixed field database, with fields for a 
name, a department code, and a telephone extension number. Records can be added, deleted, updated, 
searched for, and even sorted (using quicksort). A couple of other Dbf file options are also illustrated: 
Merge and Copy. Telephone numbers, once found, can have corresponding DTMF tones emitted. The 
main screen display is straightforward, involving double height characters. 


The application Days illustrates a Dbf database that stores peoples’ birthdays (or other days of interest), 
along with a notes field. The application maintains an in-memory index which sorts the entries by date to 
facilitate a more meaningful presentation of the contents of the database. The main screen view is 
somewhat elaborate: it can be toggled between a series of edit boxes (as in Notes) and a scrolling list (as 
in Remind). Alteration of the data takes place by direct manipulation in the edit boxes. 


Many secrets of the inner workings of the Series 3 are revealed by the application Spy, which presents a 
list of all the processes running at any one time, together with specified information, such as the number 
of cells in the allocator heap of that process, and the watermark on its stack. This list can be refreshed on 
a timer, so the application also provides an additional example of asynchronous keyboard reads. For 
more details on what can be done using Spy, see the chapter Using Spy.app in the Series 3/3a 
Programming Guide. 


Associated with Spy is a maverick application called Joker, whose main role is to instantiate all the 
special cases tested for by Spy. For example, Joker can corrupt its allocator heap on demand, in a variety 
of different ways. 


Finally, the application Iconed is a full-blown icon editor, which can be used to design icons for new 
applications (and to improve the icons shipped with the example applications). This illustrates a whole 
variety of more advanced graphics calls, as well as another set of possibilities in file-based applications. 


34 


2 WORKED EXAMPLES IN HWIF 


SS SSS SS re aE 
Getting started: the Query application 


Hello World 
Consider the following program: 


#include <p_std.h> 
#include <wlib.h> 
#include <hwif.h> 


GLDEF_C VOID main(VOID) 
€ 
WMSG_KEY key; 


uCommoninit(); 

wSetBusyMsg("Hello world",W_CORNER_BOTTOM_LEFT); 
uGetKey(&key); 

uGetKey(&key) ; 

p_exit¢Q); 

3 


This can be typed into your favourite programming editor (either inside or outside the TopSpeed 
programming environment invoked by the ts command). Or, to save time, it can be loaded from disc: this 
program is supplied in the directory \sibosdk\hwdemo\ as qui.c (with the name of this file indicating that 
it is the first version of the Query application). 


The meaning of various lines in this program is as follows: 


#include <p_std.h> This is the standard Psion header file, containing the function prototypes for 
all "simple" Plib routines (such as p_exit), as well as the typedefs for the likes 
of GLDEF_c and voip 


#include <wlib.h> This is the Wlib header file, containing the function prototypes for all Wlib 
routines, such as wSetBusyMsg, as well as the definitions of constants like 
W_CORNER_BOTTOM_LEFT and structs like WMSG_KEY 


#include <hwif.h> This is the Hwif header file, containing the function prototypes for all Hwif 
routines, such as uConmonInit, as well as the definitions of numerous constants 
and structs 


uCommonInit(); The first statement in all Hwif programs; miss this out and the above program 
will panic when it reaches the next line 


wSetBusyMsg(...); | Display the indicated text as a flashing message at the bottom left comer of the 
screen 


uGetKey(&key); Wait for a keypress event to be delivered (see below on why this line appears 
twice in the above program) 


p_exit(0); Exit the application cleanly. 


To build the program 


The next stage is to convert the above .c source file into a .img executable file. Any such conversion is 
governed by a TopSpeed project file, with extension .pr. (The .pr file is involved whether or not 
compilation takes place inside the ts system.) 


In fact, all but two of the Hwif demo applications can use the project file unnamed.pr that can be found 
in the same directory as the source modules. The exceptions are: 


= Spy which has three different source modules and therefore its own .pr file (spy.pr) 
« Remind which has its own remind.pr 


For more details on .pr files, and on the associated housekeeping batch files such as make.bat, see the 
chapter Building an Application in the General Programming Manual. 


To create qul.img, simply type make qu’. 


35 


PROGRAMMING IN HWIF 


Note that the unnamed.pr in \sibosdk\hwdemo has one extra line than the copy in \sibosdk\demo: 
#pragma linkChwif. lib) 
to ensure that the Hwif library is pulled into the link. 


Errors during linking 
However the .img file is built, a spurious pair of warnings may, regrettably, be issued by the linker: 


_ClassTable is duplicated, files involved are hwif and rlib 
_ExtCatTable is duplicated, files involved are hwif and rlib 


These warnings (which, at the time of writing, cannot be disabled) should be ignored. It is, however, 
important that the HWIF and RLIB libraries are linked in the correct order. The RLIB library is 
automatically included in the link and should not be explicitly included in any application's .pr file. 


Needless to say, any other reported errors should be carefully attended to. 


Running the application from the Series 3 System Screen 


Once the file guJ.img has successfully been built, it can be copied (using Remote Link on the Series 3 
and McLink on the PC) into a top-level \img\ directory on a Series 3. The name Qu/ will now appear 
under the Runimg icon in the System Screen. (Update the System Screen display, if need be, by pressing 
SYSTEM; you can press CONTROL -+SYSTEM to position to the Runimg icon; if perchance you have 
removed this icon, re-install it with the Jnstall standard menu command.) 


Cursor down onto the name Qu/ and press ENTER. The screen will clear completely, except for a 
message Hello world flashing at the bottom left hand corner. On any keypress, the application terminates. 


For an explanation of how to debug an application such as Qu1, see in the first instance the chapter 
Building an Application in the General Programming Manual, and for more details, the SIBO Debugger 
manual. 


Further explanation of qu1.c 


The reason why qguJ.c contains two calls to uGetkey can now be clarified. Whenever any application 
comes into the foreground, it is sent notification of this fact. For Hwif programs, this notification takes 
the form of a special keypress. The value of this keypress is CONS_EVENT_FOREGROUND, that is 0x401 (consult 
the header file p_cons.h - which is automatically #included by Awif.h.) This (generalised) keypress is 
sent to the application as soon as it starts, since when it starts, it comes into foreground. Hence the need 
for a second call to uGetKey, to make the program wait until another keypress is received, before 
terminating. 


Incidentally, the code 


GLDEF_C VOID main(VOID) 
4 


p_exit(value); 
} 


is of course equivalent to 


GLDEF_C INT main(VOID) 
€ 


return(value); 
> 


However, qul.c uses the former method since it is actually rare for Hwif applications to terminate at the 
bottom of their main routines. Instead, they usually terminate, with a call to p exit, as soon as a suitable 
menu command is received (but after first carrying out any necessary checks and/or saving data to file); 

in-line calls to p_exit are also common when errors arise during application initialisation. 


Suggestions for modifying Qu1 


= Replace the two calls to uGetKey with a loop that repeatedly waits for keypresses, and which 
terminates the program only on a designated keypress 


® Inside this loop, test for other chosen keypresses, and make calls to wsetBusyMsg with parameters 
that depend on what the keypress is 


36 


2 WORKED EXAMPLES IN HWIF 
Ses SSeS 


= Include, on various keypresses, calls to winfoMsg as well as to wSetBusyMsg; also try calls to 
p_sound, wsAlertwW, and wClientPosition (to position the application to background) 


= Instead of displaying Hello world, display the current time and/or date 


= Deliberately call p_exit with a negative parameter, to see what the effect is. 


introducing a GC 


There are many graphics calls in the Wlib library that cannot be made unless a GC (graphics context) 
exists. This includes the function gBorder that provides the standard Series 3 framing for regions of the 
screen, 


Accordingly, one step up from quI.c is for main to become (as in gu2.c) 


GLDEF_C VOID main(VOID) 
€ 
WMSG_KEY key; 


uCommonI nit); 

CreateGC(); 

gBorder(W_BORD_CORNER_4); 

wSetBusyMsg("Hello world", W_CORNER_BOTTOM_LEFT); 


FOREVER 
€ 
uGetKey(&key); 
if (key.keycode==(W_SPECIAL_KEY|'x')) 
p_exit(0); 
> 
> 


with the application-supplied routine createcc being 


LOCAL_C VOID CreateGC(VOID) 
€ 
INT hge; 


hgc=gCreateGCO(uF indMainWid()); 
if Chgc<0) 

P_exit(hge); 
> 


The GC created has default characteristics (hence the call gcreateGc0 as opposed to the more general 
gCreateGC, which takes more parameters). The one parameter that has to be passed to gCreateGco is the ID 
of the window the GC is attached to. The Hwif call uFindMainwid returns, as its name implies, the ID of 
the window filling the screen, that was created by the call to uCommoninit. 


Note that the call gcreateGco can fail. This is because it requires the Window Server to allocate additional 
resources, and it may be the case, if there are many other applications running on the Series 3, some of 
which have many windows, that the Window Server cannot satisfy this request. 

Suggestions for modifying Qu2 


= Now that a GC has been created, try out the effect of other Wlib calls, such as gDrawLine, 
gDrawBox, gBorderRect, gClrRect, gPrintText, gPrintBoxText, and ginvObloid - all (possibly) in 
response to the receipt of suitable incoming keypresses 


= Use gCreateGc instead of gCreateGcO, and experiment with the other parameters (eg the font and 
style fields of the G_cc struct required) 


= Use gSetcc to change the nature of the GC dynamically on designated keypresses (there will 
need to be a permanent record of the handle hgc of the GC created) 


= Experiment with graphics calls that, being non GC-based, require to be passed the ID of the 
relevant window: wScrollWin, wOrawTextCursor, wMakeInvisible, and wsCreateClock. 
A status window and a menu bar 


To look more like a genuine Series 3 application, the display should, where possible, sport a status 
window. This requires two steps: 


= calling the Wlib function wsEnable to display a permanent status window 


37 


PROGRAMMING IN HWIF 


= resizing the main graphics window, so that it no longer obscures the region where the status 
window is displayed. 


Provided the call to g8order is delayed until after the screen is resized smaller, the border will 
automatically appear with an appropriately reduced width. 


Note that the call wsEnabte has no visible effect unless the main window of the application has been 
resized appropriately: permanent status windows always come at the back of the window order. 


The routine to resize the main graphics window smaller is as follows: 


LOCAL_C VOID ReduceScreenSize(VOID) 
{ 
W_WINDATA wd; 


wd.extent.tl.x=wd.extent.tl.y=0; 

wd.extent .width=189; 

wd.extent .height=80; 

wSetWindow(MainWid,W_WIN_EXTENT,&wd); 

if CuErrorValue(wCheckPoint())) 
P_exit¢0): 

> 


Note the following points: 
= the static variable Mainwid stores the result of a prior call to uF indMainwWid 


= unexpectedly, it is possible for the resize to fail on account of lack of Window Server memory; 
this is because the backed-up bitmap for the reduced window size is created before the one for 
the old window size is finally discarded 


= the call to wsetwindow is not flushed straightaway because it does not return a value; in other 
words, the call may not be executed immediately. However, as the above point notes, it is 
possible that the call could fail when it does eventually execute. 
To make sure that the call is executed immediately, it is necessary to flush the Window Server 
command buffer immediately after the call is made and to check that no error is reported as a 
result; this is the point of the otherwise little-used call weheckPoint. 


= The width and height values are suitable for the Series 3 screen, or for the Series 3a or 
Workabout in Series 3 emulation mode. For a Series 3a in native mode, or a Workabout in full- 
screen emulation mode, alternative values may be more appropriate. 


The lines of code 


if CuErrorValue(wCheckPoint())) 
p_exit¢0); 


are equivalent to 


INT ret; 


ret=wCheckPoint(); 
if (ret) 
p_exit(ret); 


Both methods result in the user being notified of any error (with an appropriate error string being 
presented), and in the application being terminated in response to the error. 


The routine ReduceScreenSize is added into the developing Query application in the file gu3.c. Qu3 also 
adds in the presentation of a menu bar. There is now enough initialisation to create a separate 
initialisation routine 


LOCAL_C VOID SpecificInit(VOID) 


€ 

MainWid=uF indMainWid(); 
ReduceScreenSize(); 
CreateGC(); 
gBorder(W_BORD_CORNER_4); 
wsEnable(); 
winfoMsg("Hello world"); 
> 


38 


2 WORKED EXAMPLES IN HWIF 
a eee 


and main now simplifies to what is, in Hwif applications, its standard form: 


GLDEF_C VOID main(VOID) 
€ 
uCommonInit(); 
SpecificInit(); 
MainLoop(); 
> 


This leaves the new routine MainLoop: 


LOCAL_C VOID MainLoop(VOID) 
{ 
INT ret; 
WMSG_KEY key; 


FOREVER 
€ 
uGetKey(&key); 
if (key. keycode&W_SPECIAL_KEY) 
ManageCommand( key. keycode&(“W_SPECIAL_KEY)); 
else if (key. keycode==W_KEY MENU && !(key.modifiers&W_CTRL_MODIFIER)) 
{ 
ret=uPresentMenus(); 
if (ret>0) 
ManageCommand( ret); 
> 


} 
which is an elaboration of the lines 


FOREVER 
{ 
uGetKey(&key); 
if (key.keycode==(W_SPECIAL_KEY|'x!')) 
p_exit(0); 
> 


from qu2.c. 
Note the following points: 


= the above code ignores the key combination CONTROL+MENU which often toggles the visibility 
of any permanent status window; for this application, because the main display area (apart from 
the status window) is so empty, there is no special merit in allowing the permanent status 
window to be hidden 


= only positive return values from uPresentMenus are of interest; other values correspond to the 
user cancelling out of the menu bar by pressing ESCAPE (as well as to cases where the menu 
presentation failed due to lack of memory) 


= the program has to provide two passages to the routine ManageConmand: one following the 
presentation of the menu bar, and the other following interception of a menu accelerator when 
no menu is showing. 


It is worth mentioning a potential problem with the MainLoop routine. On the Series 3 only the lower case 
characters ‘a’ to 'z' plus the four characters '+','-’,'*' and '/' (in English language versions) are valid 
accelerators and the above code will work unambiguously. The Series 3a and Workabout, however, 
permit the uppercase alphabetic characters 'A' to 'Z'. To cater for this situation, MainLoop could be 
changed as shown below: 


LOCAL_C VOID MainLoop(VOID) 
€ 
INT code; 
INT ret; 
WMSG_KEY key; 


39 


PROGRAMMING IN HWIF 
—_——. SEE 


FOREVER 
€ 
uGetKey(&key); 
if (key. keycode & W_SPECIAL_KEY) 
€ 
code = key. keycode & ("W_SPECIAL_KEY); 
if (key.modifiers & W_SHIFT_ MODIFIER) 
code = p_toupper(code); 
ManageCommand( code); 
> 
else if (key.keycode==W_KEY_MENU && !(key.modifiers&W_CTRL_MODIFIER)) 
.¢ 
ret=uPresentMenus(); 
if (ret>0) 
ManageCommand( ret); 
> 
3 
> 


The function p_toupper is needed to ensure that the keycode is in uppercase before being passed to the 
ManageCommand routine. Note, however, that no change is required to the code concerned with selecting a 
command by highlighting its menu item and pressing ENTER (implemented by the call to uPresentMenus). 


Defining the menu bar 


The top of qu3.c is as follows: 


LOCAL_D TEXT *cmds[}= 
{ 
"ji Inches/Centimetres", 
“mMi les/Kilometres", 
"LPounds/Kilogrammes", 
"pPints/Litres", 
"fFahrenheit/Centigrade", 
"dDay of week", 
"cCombinations", 
"tTime difference", 
"hHoroscope", 
"nNow", 
"wPassword", 
"eEncrypt", 
"udecrypt", 
"sSignificance", 
"2File size", 


"XExXit", 
NULL 
F 

LOCAL_D H_MENU_DATA mdata [I= 
€ 
"Conversions",5, /* first five of above commands form the Conversions menu */ 
"Calendar",5, /* next five form the Calendar menu */ 
"Secret" ,3, /* then three for the Secret menu */ 
"Special",3, /* then three for the Special menu */ 
NULL 
i; 


GLDEF_D TEXT ** _cmds=(&cmds{0}); 
GLDEF_D H_MENU_DATA * _mdata=(&mdata[0]); 


with the statics _cmds and _mdata defining the contents of the menu bar and of each pulldown menu. 


The contents of ManageCommand 


Evidently, the real core of the application is to be found in ManageCommand, and in routines called therein. 


40 


2 WORKED EXAMPLES IN HWIF 
SSS 


As far as gu3.c is concerned, the contents of ManageCommand are just as follows: 


LOCAL_C VOID ManageCommand(INT keycode) 
€ 
INT index; 
TEXT buf [£40]; 


switch (keycode) 
€ 
case 'x!: 
p_exit(0); 
default: 
index=uLocateCommand( keycode) ; 
if Cindex>=0) 
€ 
p_atos(&buf (0],"You chose '%s'", cmds [index] +1); 
winfoMsg(&buf [0] ); 
> 


> 


As can be seen, only the Exit command functions properly. All other commands give rise to an 
information message of the form 


You chose 'Fahrenheit/Centigrade’. 


Note the use of the Hwif library routine uLocateCommand to convert between the accelerator of a menu 
command (which is what is returned by uPresentMenus) and the index of the menu command in the table 
identified by _cmds. This provides a simple means of recovering the text for the chosen menu command. 
Note also the need to test whether the passed keycode corresponds to any of the menu commands (ie the 
test on whether the return value from uLocateCommand is non-negative). 


Suggestions for modifying Qu3 


= Provide code to display the current day name, in response to the Day of week menu command 
(use eg winfoMsg) 


= Display the current time, in response to the Now menu command 


= In response to the File size menu command, display the size of qu3.img, determined by a run- 
time call (the full path name of gu3.img will be stored, as a zero terminated string, at the Epoc 
Static DatCommandPtr - as can be verified inside the Debugger) 


= Provide code to toggle the permanent status window on receipt of CONTROL+MENU. 


Supplying an icon 


Qu3 suffers from having an empty hole, in its status window, where an icon should be. (The "empty 
hole" is actually the default icon.) This shortcoming is in fact shared by Qu and Qu2, in that any 
temporary status window displayed while they are in foreground also lacks a proper icon. The problem is 
solved by Qu4. The critical difference is that Qu4 has its own .afl file. 


The content of qu4.afl is just the single line 
query.pic 


When Qu4 is being linked, the fact that there is an .afl file is picked up by the TopSpeed make system (in 
the part specially customised for the Epoc system), and any files listed in this file are joined together, 
with the ordinary outcome of linking, to produce the final .img file. In this case, a copy of the icon file 
query.pic is built into the final executable qu4.img. 


First examples in presenting dialogs 


Qu4 advances from Qu3, not only by having a proper icon, but also by having some proper dialogs - one 
each for the commands File size, Day of week, and Now. 


41 


PROGRAMMING IN HWIF 


ManageCommand accordingly grows: 


LOCAL_C VOID ManageCommand(INT keycode) 
€ 
INT index; 
TEXT buf [40]; 


switch (keycode) 
€ 
case 'd!: 
DayOfWeek(); 
break; 
case 'n!: 
TimeNow(); 
break; 
case 'z': 
FileSize¢); 
break; 
case 'x!: 
p_exit¢0); 
default: 
index=uLocateCommand( keycode) ; 
if Cindex>=0) 
€ 
p_atos(&buf [0] ,"You chose ~%s'", cmds [index] +1); 
winfoMsg(&buf [0] ); 
d 


> 
and the code for FileSize is 


LOCAL_C VOID FileSize(VOID) 
€ 
TEXT fname [P_FNAMESIZE+2] ; 
H_DI_FSEL fsel; 
P_INFO info; 
TEXT buf [30]; 


fname [0] =0; 
fsel. fname=(&fname [0] ); 
fsel.flags=H_FILE_PICK_SELECTOR; 
FOREVER 
€ 
if (udpenDialog("Find file size")) 
return; 
if CuAddDialogI tem(H_DIALOG_FSEL,"File:",&fsel)) 
return; 
if CuRunDialog()<=0) 
return; 
fname (1+fname [0] ] =0; 
if (!uErrorValue(p finfo(&fname[1] ,&info))) 
€ 
p_atos(&buf (0],"File size is %lu bytes", info.size); 
winfoMsg(&buf [0] ); 
> 


> 
The call that works out, amongst other things, the size of the specified file, is 
p_finfoC&fname[1] ,&info); 


with the size, in bytes, being written to the size member of the passed P_INFo struct. The code that 
displays the file size is 


p_atos(&buf[0],"File size is 4lu bytes", info.size); 
winfoMsg(&buf [0] >; 


42 


2 WORKED EXAMPLES IN HWIF 
SSS 


After the user completes the dialog, the file size is displayed, and the dialog is presented again, for the 
user to choose another file. The FOREVER loop in FileSize only terminates 


= if the user presses ESCAPE to cancel the dialog - in which case uRunDialog returns zero 


= — if there is insufficient memory for any of the calls defining or presenting the dialog - in which 
case the corresponding call to udpenDialog, uAddDialogitem, Of uRunDialog returns a negative 
number. 


The filename chosen by the user is written to the buffer fsel. fname as a leading byte counted string 
(BCS). However, the function p_finfo requires a zero terminated string (ZTS). Hence the conversion 


fname (1+fname [0] } =0; 


before the call to p_finfo. Since a filename as written by a filename selector can have, in general, up to 
P_FNAMESIZE (128) bytes, and since the above code writes an extra byte beyond the end of this, the result 
is that fname has to be declared to be at least P_FNAMESIZE+1 bytes long. In the interests of even byte 
alignment, it has actually been declared as P_FNAMESIZE+2 bytes long. 


It is necessary to add the line 
#include <p_file.h> 


to the top of qu4.c, since this is where the definitions of the constant P_FNAMESIZE and the struct P_INFO 
are to be found. This header file also contains the function prototype for p_finfo. 


The call u€rrorValue made around the result of p_finfo presents a suitable error message, if the size of the 
file cannot be found. There is no need for corresponding calls around the results of udpenDialog, 
uAddDialogItem, and uRunDialog, since these latter functions have error-notification built into them. Error 
notification is standard for all but the most primitive of the Hwif library routines; however, because of 
the generality of the Plib and Wlib functions, such error-notification code is not supplied in their case. 


In fact, it would be a very rare case indeed for the above call to p finfo to fail. This is because the 
filename selector item in the dialog automatically validates its contents, before allowing the dialog to 
terminate. Nevertheless, it is theoretically possible for the file to be deleted in between the calls 
uRunDialog and p_finfo (bear in mind the multi-tasking nature of the Series 3; more likely, an SSD might 
be removed, or, for the case of a file selected on REM::, a remote link might become broken). Hence the 
call to uErrorValue. 


In the above code, the filename is initialised to have zero length, by the code 
fname [0] =0; 


This means that the filename selector will position itself initially to the default path of the application. 
Since nothing has been done to set this up, the file shown in the dialog, when it first appears, will most 
likely be something in the root directory of the default drive - perhaps the file sys$stub. img. If there are 
no files in this directory, the filename selector will say so, and will not allow the user to terminate the 
dialog (apart from cancelling it) until transitioning to a directory in which files do exist. 


Suggestions for modifying FileSize 
= Display the time the file was last modified, instead of its size 
= To see the effect of the uErrorValue call, introduce a p_steep before the call to p_finfo and use 
this delay to pull out an SSD on which a filename has been selected. 


A date editor in a dialog 
The routine DayOfwWeek contains an example of a date editor in a dialog: 


LOCAL_C VOID DayOfWeek(VOID) 
€ 
P_DAYSEC ds; 
H_DI_DATE date; 
TEXT buf [40]; 
H_DI_TEXT txt; 


43 


PROGRAMMING IN HWIF 


SetUpD iDate(&date, &ds): ( 
do 
€ 
p_nmday(&buf [1] ,p_wkday(ds.day)); 
buf [0]=p_slen(&buf [1] ); 
txt.str=(&buf [0] ); 
txt. type=H_DTEXT_ALIGN_LEFT; 
if CudpenDialog("Find day of week")) 
return; 
if CuAddDialog! tem( H_DIALOG_DATE, "Date", &date)) 
return; 
if CuAddDialogItem(H_DIALOG_TEXT,"Day of week", &txt)) 
return; 
> while CuRunDialog()>0); 
> 


In contrast to FileSize, which displays its result as an information message separate from the dialog, 
(using the call winfoMsg), DayOfWeek displays its result inside the dialog, as a text item included in the 
dialog: 

buf [0]=p_slen(&buf [1] ); 

txt.str=(&buf [0] ); 

txt. type=H_DTEXT_ALIGN_LEFT; 

if CuAddDialogItem(H_DIALOG_TEXT,"Day of week", &txt)) 

return; 


The name of the day in the week is determined, as a ZTS, by the calls 
p_nmday(&buf [1] ,p_wkday(ds.day)); 

and the string is converted into the BCS form required by the H_D1_TEXT struct by the code 
buf (0]=p_slen(&buf [1] ); 


The day itself is stored, in the ULONG ds.day, as a day number since the beginning of 1900. This is the 
form required by both the call p_wkday and the struct H_DI_DATE. It is initialised to today by the routine 
SetUpD iDate: 


LOCAL_C VOID SetUpDiDate(H_DI_DATE *pdate,P_DAYSEC *pds) 
€ 
ULONG sdate; 


sdate=p_date(); 
p_sttods(&sdate, pds); 
pdate->value=(&pds->day); 
pdate->low=0; 
pdate->high=H_LAST_DAY; 
> 


It is necessary to add the line 
#include <p_date.h> 


to the top of qu4.c, since this is where the definitions of the struct p_DAYsEc and the function p_sttods are 
to be found. 


Choice lists and the time-text functions 


The routine TimeNow contains examples of choice lists in a dialog - five choice lists in all, in fact all just 
with the two choices "No" and "Yes" and added into the current dialog by the utility function 
AddNoYesChoiceList: 


LOCAL_C INT AddNoYesChoiceList(TEXT *pmt,UWORD *pval) 
€ 
return(uAddChoiceList(pmt,pval, "No", "Yes", NULL)): 
> 


2 WORKED EXAMPLES IN HWIF 
oo SSeS 


LOCAL_C VOID TimeNow(VOID) 
€ 
INT values; 
H_DI_TEXT txt; 
TEXT buf £48]; 


txt.str=(&buf [0] ); 
do 
€ 
if CuOpenDialog(NULL)) 
return; 
uZTStoBCS(&buf [0] ,"Time is now"); 
txt. type=H_DTEXT_ALIGN_CENTRE; 
if (uAddD ialogI tem(H_DIALOG_TEXT,NULL,&txt)) 
return; 
values=0; 
if (ttMonth==2) 
values=H_TIME_FORMAT_MONTH_NAME; 
if (ttDay==2) 
values |=H_TIME_FORMAT_DAY_NAME; 
if (ttSuffix==2) 
values |=H_TIME_FORMAT_SUFFIX; 
if (ttCentury==1) 
values |=H_TIME_FORMAT_NO_CENTURY; 
if (ttSeconds==1) 
values |=H_TIME_FORMAT_NO_SECONDS; 
hTTSetFormat(ttH, values); 
hTTSetTime(ttH,H_TIME_SET_NOW,NULL); 
buf [0] =hTTSenseString(ttH,H_TIME_SENSE_BOTH, &buf [1]); 
txt. type=H_DTEXT_ALIGN_CENTRE|H_DTEXT_UNDERLINE; 
if (uAddD falogI tem(H_DIALOG_TEXT,NULL,&txt)) 
return; 
if CAddNoYesChoiceList("Give month name",&ttMonth)) 
return; 
if (AddNoYesChoiceList("Give day name",&ttDay)) 
return; 
if (AddNoYesChoiceList("Use date suffix", &ttSuffix)) 
return; 
if (AddNoYesChoiceList("Show century", &ttCentury)) 
return; 
if CAddNoYesChoiceList("Show seconds", &ttSeconds) ) 
return; 
> while CuRunDialog()>0); 
> 


The live variables for the five choice lists are five statics defined at the top of gu4.c, and all given initial 
values reflecting the defaults built into the Hwif time-text utility functions: 


LOCAL_D VOID *ttH; 

LOCAL_D UWORD ttMonth=1; 
LOCAL_D UWORD ttSuffix=1; 
LOCAL_D UWORD ttDay=1; 
LOCAL_D UWORD ttCentury=2; 
LOCAL_D UWORD ttSeconds=2; 


The textual form of the time is generated by the time-text channel tt opened by the following call at the 
end of Specifictnit: 


ttH=hTTOpen(); 
The text is actually generated by the calls 


hTTSetFormat(ttH, values): 
hTTSetT ime( ttH, H_TIME_SET_NOW,NULL); 
buf [0] =hTTSenseString(ttH,H_TIME_SENSE_BOTH, &buf{1]); 


where vatues has been built up as a combination of the present values of the ttxxx variables. 


Whereas the results of FileSize and DayOfweek only persist until the user cancels the dialog, the result of 
TimeNow persists throughout the lifetime of the application. This is because the ttxxx variables are statics. 


45 


PROGRAMMING IN HWIF 
— SSS 


A note on the start-up heap 


There is no special need to test for the result of the call httopen made in Specificinit. No Window 
Server resources are required for a time-text channel; the only allocating that needs to be performed for it 
is out of the application's own heap. Since the call is made during application initialisation, it can be 
assumed that, if the application has been allowed by the OS to run at all, there will be sufficient memory 
for the call to succeed. 


This point is worthy of some further explanation. Using either the Debugger or the Spy application, it 
can be seen that, when running, Qu4 has only around 0x300 bytes allocated from its heap. However, the 
default value for the minimum heap of an application is 0x80 paragraphs, ie 0x800 bytes. 


Further investigation will reveal that the minimum segment size for Qu4 is some 0x19c0 bytes, made up 
as follows: 


0x1000 stack 
0x800 minimum heap 
Oxic0 Static data 


These figures may be confirmed by running the tool edump on qu4.img: 
edump qu4 


When the OS is instructed to try to run Qu4, it first has to allocate the data segment of 0x19c0 bytes. If it 
fails to do so, the application is not allowed to run, and an out-of-memory notifier is presented. But if it 
succeeds, the 0x19c0 bytes are guaranteed to remain available throughout the lifetime of the application. 
Hence the guarantee that the call to hTTopen will never fail. 


Clearly, Qu4 is an extremely anti-social application, hogging much more heap (not to mention much 
more stack) than it needs. Such behaviour would be unacceptable in any commercial application. One 
penalty the application incurs, upon itself, is that the OS will sometimes refuse to run it, even though 
there is sufficient memory available for its actual requirements - the point being that there is insufficient 
memory available for its stated requirements. 


Incidentally, the start-up heap for an application can be customised by means of including a line such as 
set heapsize=0x40 

in the .pr project file governing how the application is built. The stack can be specified by means of a 

different value of epocinit. 

Further comments on edump 


Another piece of information that edump gives is the size of any additional files built into the specified 
image. Thus the result of running edump on qu4.img includes the line 


Add 1 offset,len = 0040 (bytes), 0074 (bytes) 
whereas no such line is given for qu3.img. This additional file, of size 0x74 bytes, is of course the copy 
of the icon query.pic. 
Suggestions for modifying Qu4 

= Make the results of FileSize and DayOfweek persistent in the same way as the result of TimeNow is 


= For some dates (eg Wednesday 26th September), the textual representation generated in TimeNow 
can end up too wide to fit properly within the widest dialog that is allowed; look out for such 
cases and abbreviate the text suitably (use abbreviated versions of the day and/or month names) 


= Produce a customised project file gu4.pr including a line defining the start-up heap more 
appropriately; confirm the result using edump. 
From .img to .app 


Although Qu4 has an icon built into it, it is not yet able to be installed in its own right as an application 
in the System Screen. For this to be possible, an application also needs to have a shd (shell data) file 
built into it. 


For Query, the source of the shd file is query.ms, which consists solely of the line 


Query 


46 


2 WORKED EXAMPLES IN HWIF 
SSeS 


This is actually an abbreviated form of a three-line file: 


Query 


0 


in which the third line gives the type of the application. A type of zero means that the application is non 
file-based, and consequently has no associated files. 


The file query.shd can be produced from query.ms by the command 
makeshd query 
and then the file query. shd is joined into the final executable by being listed in query.afl. 


Up to three files can be specified in an .afl file. Whereas the icon of an application can be placed into any 
of the three slots, the shell data has to be placed into the third slot. Thus the contents of query.afl become 


query.pic 
query.rsc 
query.shd 


where query.rsc is any small file (preferably a zero-length file). 
Running edump on query.img produces the following three lines of output (among others) 


Add 1 offset, len 
Add 2 offset, len 
Add 3 offset, len 


0040 (bytes), 0074 (bytes) 
QOCO (bytes), 0000 (bytes) 
00CO (bytes), 0024 (bytes) 


Whereas an application without shell data is usually copied to an \img\ directory on the Series 3, one 
with shell data is usually copied to an lapp\ directory, and renamed from .img to .app at the same time. 
Thereafter, the application can be installed, using the Install application command in the System Screen. 


Once installed, it can be run in the same way as any of the built-in applications is. Further, an application 
button such as CONTROL+CALC can be assigned to it, if desired. 
The floating point emulator sys$8087.Idd 


Before Query can be run successfully, the Series 3 needs to be able to locate the floating point maths 
emulator, sys$8087.ldd. This is because query.c contains lines such as 


DOUBLE fahr; 


fahr=32; 
which, innocent as it may seem, requires the presence of sys$8087.ldd. 


The simplest way to ensure the Series 3 can locate this emulator is to place a copy of it in the same 
directory as the application itself. Thus if query.img is copied to m:\app\query.app on the Series 3, a 
copy of sys$8087.ldd could be copied into this same directory, m:\app\. A copy may be found in 
\sibosdk\lib\ on the PC. 


In fact, of the example applications, Query is the only one which requires the presence of the emulator. 


The built-in applications avoid requiring to use the emulator, since they replace the likes of the above 
lines of code by the following 


DOUBLE fahr; 

WORD temp; 

temp=32; 
p_itof(&fahr,&temp); 


which although it looks more cumbersome, actually produces leaner code overall. 


Debugging a .app application 


The mechanism for debugging a .app application is virtually the same as debugging a .img application. 
In neither case is there any need to copy the application onto the Series 3 by hand. 


The only complication concerns the need to pass a suitable command line to file-based applications. This 
is considered later. However, non file-based applications, such as Query, can be run without any 
command line being passed to them. 


47 


PROGRAMMING IN HWIF 


Some responsibilities of being a .app 


In general, an application intended to be capable of being installed in the System Screen should always 
make a call to hCrackCommandL ine in its SpecificInit routine (or equivalent). This is true whether or not 
the application is file-based. If no call to hCrackConmandLine is made, the Epoc static DatstatusNamePtr 
will be left at its default value of zero, and it will, accordingly, be fruitless for a user to assign an 
application button (such as CONTROL+CALC) to this application. 


However, any application that calls hcrackCommandL ine must explicitly test for system messages of (at 
least) the Shutdown variety (assuming the application has not added in 4000 to its shell data type, to 
prevent such messages ever being sent). This means that the top of MainLoop in query.c has to have the 
form 


LOCAL_C VOID MainLoop(VOID) 
¢ 
WMSG_KEY key; 


FOREVER 
€ 
uGetKey(&key); 
if (key. keycode&W_EVENT_KEY) 
€ 
if (key. keycode==CONS_EVENT_COMMAND) 
p_exit(0); 
> 
else ... 


Menu command look up - by accelerator or by index? 


Query differs from Qu4 in the way the switch statement in ManageCommand is constructed: in place of 


LOCAL_C VOID ManageCommand(INT keycode) 


€ 

switch (keycode) 
€ 

case 'd': 
DayOfWeek(); 
break; 

case 'n': 
TimeNow(); 
break; 

case 'z': 
FileSize(); 
break; 

case 'x!s: 
p_exit¢0); 
3 

> 


there is, effectively, 


LOCAL_C VOID ManageCommand(INT index) 

{ 

switch (index) 
€ 

case 5: 
DayOfWeek(); 
break; 

case 9: 
TimeNow(); 
break; 

case 14: 
FileSize(); 
break; 

case 15: 
p_exit¢0); 
> 


48 


2 WORKED EXAMPLES IN HWIF 


ar 


and instead of ManageConmand being called in the simple context 


LOCAL_C VOID MainLoop(VOID) 
€ 
INT ret; 
WMSG_KEY key; 


FOREVER 
€ 
uGetKey(&key); 
if (key. keycode&W_SPECIAL_KEY) 
ManageCommand( key. keycode&(~W_SPECIAL_KEY)); 
else if (key. keycode==W_KEY_MENU && !(key.modifiers&W_CTRL_MODIFIER)) 
€ 
ret=uPresentMenus(): 
if Cret>0) 
ManageCommand(ret); 
} 


> 
there is now one extra layer to navigate between MainLoop and ManageCommand: 


LOCAL_C VOID TryExecuteCommand(INT keycode) 
cf 
keycode=uLocateCommand( keycode); 
if (keycode>=0) 
ManageCommand( keycode); 
> 


LOCAL_C VOID MainLoop(VOID) 
€ 
INT ret; 
WMSG_KEY key; 


FOREVER 
€ 
uGetKey(&key); 
if (key. keycode&W_SPECIAL_KEY) 
TryExecuteCommand( key. keycode&(“W_SPECIAL_KEY)); 
else if (key. keycode==W_KEY_MENU && !(key.modifiers&W_CTRL_MODIFIER)) 
€ 
ret=uPresentMenus(); 
if (ret>0) 
TryExecuteCommand( ret); 
> 


> 


The two mechanisms are obviously equivalent in general terms. However, the latter approach has been 
adopted throughout all the example applications. The following points can be cited in its favour: 


= quite often, several commands can be grouped together and executed more efficiently, passing as 
a parameter to a common routine the command index (possibly less some base value) 


= — the switch statement on index is completely dense, and hence compiles much more leanly than a 
switch statement on accelerator 


= the accelerator of a menu command is a less central aspect of it than its position in the menu bar; 
it is better to switch on a variable of greater importance than on one which is virtually an 
accident 


= this method is language-independent: the accelerators can be changed for a foreign-language 
version, without having to re-compile the ManageCommand routine. 


In practice, the numerical values of the command indices do not appear explicitly in code; rather, they 
are hidden through a sequence of #defines. See query.c for the details. 


49 


PROGRAMMING IN HWIF 


Example of floating point editor 


The routine Temperatures called from ManageCommand to implement the conversion between Fahrenheit and 
Centigrade demonstrates floating point editors in dialogs: 


LOCAL_C VOID Temperatures(VOID) 
€ 
DOUBLE fahr; 
DOUBLE cent; 
H_DI_FLOAT ffahr; 
H_DI_FLOAT fcent; 
INT index; 


fahr=32; 
cent=0; 
ffahr.value=(&fahr); 
ffahr. low=(-17968) ; 
ffahr .high=18032; 
fcent.value=(&cent); 
fcent. lLow=(- 10000); 
fcent .high=10000; 
FOREVER 
€ 
if CuOpenDialog("Convert temperature")) 
return; 
if CuAddDialog] tem(H_DIALOG_FLOAT,"Fahrenheit",&ffahr)) 
return; 
if CuAddDialogItemCH_DIALOG_FLOAT,"Centigrade",&fcent)) 
return; 
index=uRunDialog(); 
if Cindex<=0) 
break; 
if Cindex==2) 
€ 
cent=( fahr-32)*5/9; 
Clip(&cent); 
} 
else 
€ 
fahr=32+cent*9/5; 
Clipc&fahr); 
> 


> 


As the variable names suggest, the current value in Fahrenheit is stored in fahr, and the current value in 
Centigrade is stored in cent. There are two floating point editors, with fahr and cent being the live 
variables. Appropriate maxima and minima are set up in each case. 


The variables fahr and cent are initialised to 32 and 0 respectively. Each time the user presses ENTER, one 
or other of these variables is sensed, and the other is recalculated. Which is which depends on where the 
user has left the highlight in the dialog. Thus if the user has cursored the highlight down to the 
Centigrade line and typed in a new value there, before pressing ENTER, the call uRunDialog returns 3 (the 
counting starts at 1 for the title line in the dialog) and hence fahr is calculated anew, from the latest value 
of cent. 


Example of numeric editor 


The routine clip alters the calculated value of eg fahr or cent so that it only features a specified number 
of decimal points. (Currently, there is no Hwif mechanism for having floating point editors perform such 
a clipping themselves.) The number of decimal points is governed by the static variable ndp, which is 
initially 2. The Significance menu command allows the user to alter this: 


50 


2 WORKED EXAMPLES IN HWIF 


OO eS 


LOCAL_C VOID ChangeNdp(VOID) 
€ 
LONG indp; 
H_DI_NUMBER num; 


if CuOpenDialog("Level of significance")) 
return; 

indp=ndp; 

num. value=(&lndp); 

num. low=0; 

num. high=4; 

if (uAddDialog! tem(H_DIALOG_NUMBER,"Decimal places", &num)) 
return; 

if CuRunDialog()<=0) 
return; 

ndp=(WORD) Lndp; 

CalcSmall¢); 

> 


Note the requirement to have a Lone variable for the live variable of the numeric editor. This explains 
why a copy of ndp has to be made in the automatic variable tndp. 


The routine calcSmal| recalculates some constants that are used in calls to ct ip. 


Examples of other dialog items 

See the following routines in query.c for examples of other types of items in dialogs: 
time editors TimeDifference 
action buttons Horoscope, Combinations 
secret input boxes _EnterPassword 


text editors EncryptMessage (non-scrolling), DecryptMessage (scrolling). 


Suggestions for modifying Query 
s Add at least one more conversion routine. 
= Call hDtgPosition to position at least one dialog other than in the screen centre. 


= Eliminate the need for the floating point emulator, by using routines such as p_fadd instead of 
direct manipulation of floating point numbers. Compare the size of the executable produced with 
that of the original query.app. 


= Try to improve on the rather crude scheme in MakeReadable and MakeUnreadable, called 
respectively by EncryptMessage and DecryptMessage, to convert between a short, totally 
unreadable string of characters in the complete range of values 0 to 255 (as returned by 
p_encrypt), and a longer string with values in the range 32 to 111. 


SS EEE ee SS ae ee Eee 


Getting serious: the Tables application 


Whereas Query contains a collection of dialogs with little unifying principle, Tables contains a collection 
of dialogs all working around a common aim. This aim is to produce a revision aid for someone trying to 
learn some multiplication tables. 


What the dialogs allow to be altered is the following aspects of the state of the application: 
= how much time the user has in which to answer any multiplication question posed 
= whether the tables end at 12 (as in 3 times 12, 7 times 12, and so on), or at 10, or wherever 


= whether the questions posed all come from the same multiplication table, or from a variety, and 
in the latter case, the range of tables covered 


= the running total score of correct answers can be reset to zero. 
As well as containing the code to present these dialogs, Tables contains code to: 


i es eS 
51 


PROGRAMMING IN HWIF 


= record the state of the application in an environment variable on exit 

® — initialise the application appropriately, on start up, from this environment variable 
= calculate and pose random multiplication questions 

= present an edit box to receive the user's response 

= — simultaneously, count down a timer and progressively fill in a bar gauge display 

= present feedback to the user on whether the answer proffered is correct. 


Tal contains the dialogs and the environment variable code, but is otherwise devoid of any significant 
screen display. Ta2 adds the display of the score so far and the range of values being tested; an animated 
action button resides in the middle of the remainder of the screen. Ta3 actually poses random 
multiplication problems, and provides an edit box to receive the user's response. Tables itself adds in the 
timer, and presents the animated bar gauge display of the time elapsed. 


The state of the application 


This is recorded in a static instance, state, of the following struct: 


typedef struct 
{ 
UWORD TableEnd; /* where tables end */ 
UWORD MaxTable; /* maximum table to test */ 


UWORD Tested; /* number of questions since last reset */ 

UWORD Correct; /* number of correct answers since last reset */ 
UWORD Timing; /* number of seconds allowed for an answer */ 
UWORD Mode; /* which table is currently being tested */ 

3} TSTATE; 


with values being initialised, the very first time, by the statement 
LOCAL_D TSTATE state=(12,12,0,0,5,13; 


The value 1 for the Mode field has the special meaning that all tables are to be tested (from 2 up to 
MaxTable). 


The Tested and Correct fields are reset to zero, provided the user responds affirmatively to a query 
dialog, in the routine ResetScore. 


The TableEnd and MaxTable fields are presented for editing, using numeric editors, in the routine 
ChangeLimits. 


Another numeric editor, in the routine changeTiming, allows the user to alter Timing. 


The routine ChangeMode allows the Mode field to be changed. This uses a choice list whose contents are 
dynamically defined - they vary from "2 times table” up to "n times table", where 7 is the current value 
of MaxTable, but also always include "All tables": 


LOCAL_C VOID ChangeMode(VOID) 
€ 
H_DI_CHOICE ch; 
INT jz 
TEXT buf (201; 


if (uQpenDialog("Mode"')) 
return; 

if (uBeginDCL(&ch)) 
return; 

if CuGrowDCL(&ch, "ALL tables")) 
return; 


for (j=2; j<=state.MaxTable; j++) 
€ 
p_atos(&buf (0) ,"%d times table", j); 
if CuGrowDCL(&ch, &buf [0] )) 
return; 
> 


52 


2 WORKED EXAMPLES IN HWIF 
_—_—_— eee 


if (uAddDCL("Test which tables", &state.Mode,&ch)) 
return; 

uRunDialog(); 

3 


Using an environment variable 


Instead of simply calling p_exit on receipt of the Exit menu command (in the manner of Query), the 
following code is executed: 


LOCAL_C VOID ExitApplication(VoID) 
€ 
uErrorValue(p_setenviron(EnvName, ENV_NAME_LEN,&state,sizeof(TSTATE))); 
p_exit(0); 
> 


The code in MainLoop that responds to Shutdown messages from the System Screen also has to change to 
call ExitApplication: 


LOCAL_C VOID MainLoop(VOID) 
€ 
WMSG_KEY key; 


FOREVER 

€ 

uGetKey(&key); 

if (key. keycode&W_EVENT_KEY) 
€ 
if (key. keycode==CONS_EVENT_COMMAND) 

ExitApplication(); 

> 


There are actually two sorts of environment variables on the Series 3: 
= those whose names and values are each ZTSs 
= those whose name or values are other than a ZTS. 


Correspondingly, there are two sets of routines for reading, writing, searching, or deleting environment 
variables. (In fact, the ZTS-related routines just layer above the more general ones.) 


In this case, what is being stored in the environment variable is the content of the state struct - which is 
bound to contain embedded zeros, and so the more general routine p_setenviron has to be used, as 
opposed to the notionally simpler p_setenv. Accordingly, both the name and the value of the environment 
variable have to be passed in the form buf, Len. 


Since the name of the environment variable is obviously the same whether the variable is being set (on 
application exit) or being read (on application start-up), this has been hidden away using the static 
variable EnvName (statically initialised) and the #define ENV_NAME_LEN. 


Memory consumption by environment variables 


The only error that needs to be considered, on writing the environment variable, is lack of memory - 
either because the limit of 4K allocated for environment variables has already been reached, or because 
system memory is generally exhausted. The call ufrrorValue around p_setenviron above informs the user 
should this transpire. 


As a general principle, applications should only make sparing use of environment variables (otherwise 
they may even detract from the performance of some of the built-in applications). In order to preserve 
larger amounts of data between different invocations of an application, the data should be written to file - 
either with or without the explicit knowledge of the user. 


Note that the name of an environment variable should not, unless authorised by Psion, contain a '$' 
character. See the Environment variables on the Series 3 section of the Series 3 Programming Overview 
chapter of the Series 3/3a Programming Guide for further information on this important naming 
convention for environment variables. 


53 


PROGRAMMING IN HWIF 


Complications on reading environment variables 


Programs using environment variables should always bear in mind that it is theoretically possible for 
another application to trash the variable - as a result of using another environment variable of the same 
name. In particular, the length of the variable may turn out to be other than what is expected. For this 
reason, the buffer to receive the environment variable should always be declared as having (at least) 
P_ENVMAX (256) bytes. (In order to access the definition of p_ENVMAX, a program must #include the file 
p_sys.h.) Hence 


LOCAL_C VOID ReadState(VOID) 
€ 
INT len; 
UBYTE buf [P_ENVMAX] ; 


len=p_getenviron(EnvName, ENV_NAME_LEN, &buf [0] ); 
if (ten<0 |{ lent=sizeof(TSTATE)) 
return; /* make do with the statically initialised values */ 
p_bcpy(&state, &buf [0] ,sizeof(TSTATE)); 
> 


The case when ten is returned negative corresponds to the environment variable not existing - as will be 
the case the first time the application is run. 


Suggestions for enhancing Ta1 


= When MaxTable is large, the amount of memory required by the dynamic choice list in the dialog 
in ChangeMode can become considerable. Using either the Debugger or Spy, verify that this is the 
case, and try to re-design the dialog to require less RAM. 


= If the user presses PSION+ESCAPE, Tal is terminated without having any chance to save its state 
to an environment variable. Prevent this from happening. 


= Strictly speaking, the code in Readstate above can be caught out by a rogue program which 
writes its own environment variable, with the same name and with the same length of data, but 
with inappropriate values for the individual fields. Write such a rogue program to demonstrate 
this fact, and consider amending Readstate to take better precautions. 


Laying out information on the screen 


Ta2 goes beyond Ta! in that it lays out its current state on the screen, for the user to see. 


For example 


Score! 4 correct out of 9 eee 
Testing 7 times table 2x48 


(up to 7 times 12) Tables 


Press to test Zid 
mM 
Tha 23 


There are no hard and fast rules for designing such a layout, but there certainly are easier and harder 
ways of going about achieving a given layout (once one has been decided upon). The following 
discussion may be read as an example of how to achieve a layout such as that shown in the above screen 
dump. 


The three lines of text at the top of the screen share the following features: 
# they are centred in the main window (apart from the status window) 
= they need to be smoothly updated when there is a change in any of the values shown. 


Both these reasons argue in favour of using gPrintBoxText to draw the lines. Not only can this function 
automatically centre text, it also takes care of the smooth screen update. 


To explain the latter point more fully, consider what has to happen to the display of the second line down 
when the user changes from testing the 12 times table to testing the 7 times table. Not only does new text 
have to be drawn, some areas just outside the limits of the new text have to be cleared - since the new 
text is slightly narrower than the old. 


54 


2 WORKED EXAMPLES IN HWIF 
eS 


Naively, the way to accomplish the above would be as follows: 
= first, clear the area of screen where the old message was drawn 


= second, draw the new text. 


However, this can give rise to a noticeable and annoying screen flicker. This can be especially annoying 
if, as often happens, the text is updated when there is actually no change in it. 


The routine gPrintBoxText avoids these problems by simultaneously clearing pixels and drawing to them, 
sweeping along in a horizontal pass. Pixels are written to if new text is to appear there, and are otherwise 
cleared. 


The three top lines are written by a common routine: 


LOCAL_C VOID DrawLine(INT j,TEXT *pb) 
{ 
P_RECT box; 


box.tl.x=4- 

box. br .x=189-4; 

box. tl .y=4+9*j: 

box.br.y=box.tl .y+9; 
gPrintBoxText(&box,8,G_TEXT_ALIGN_CENTRE,C,pb,p slen(pb)); 
d 


which is called as follows 


LOCAL_C VOID DisplayScores(VOID) 
r¢ 
TEXT buf [40]; 


p_atos(&buf(0],"Score: %u correct out of du" ,state.Correct, state. Tested); 
DrawLine(0,&buf [0] ); 
> 


and 


LOCAL_C VOID DisplayMode(VOID) 
€ 
TEXT *pb; 
TEXT buf [40]; 


if (state.Mode==1) 
pb="Testing all tables"; 
else 
ca 
p_atos(&buf [0] ,"Testing %d times table",state.Mode); 
pb=(&buf [0] ); 
> 
DrawLine(1,pb); 
> 


and 


LOCAL_C VOID DisplayLimits(VvoID) 
{ 
TEXT buf [40]; 


p_atos(&buf[0],"Cup to %d times %d)", 

(state.Mode==1? state.MaxTable: state.Mode),state.TableEnd); 
DrawLine(2,&buf [0] ); 
> 


In all cases, the box drawn to has height nine pixels. This allows one pixel of leading between lines, in 
addition to the font height of eight pixels. Since at least one pixel has to be reserved for descenders (such 
as the bottom pixel in the p of up), the maximum allowed value for the ascent parameter to gPrintBoxText 
is 8 - as in the above routine. This means in fact that the extra pixel of leading goes above the 
corresponding line of text (not that it matters in this case). 


55 


PROGRAMMING IN HWIF 
Se 


Collectively, the text respects a border of four pixels all around. The value of 189 is the full screen width 
(240 pixels) of the Series 3 and Workabout, or of the Series 3a in Series 3 emulation mode, less the width 
of the status window (51 pixels). 


Since there is no need for a margin parameter in this case, it is left at zero. 


To take advantage of the increased screen width of the Series 3a in "native" mode (480 pixels), the sizing 
and positioning of the text would need to be changed and the above calculations reworked. 


Laying out an action button and its associated text 
The text Press Enter to test is rather harder to position, because of the embedded action button. 


Start by considering the horizontal direction. 38 pixels is a good width for the action button (this is the 
width of the action buttons in dialogs). There are five characters in Press, say six to include one trailing 
space. This translates to around 36 pixels, allowing 6 characters per pixel. Likewise, there are seven 
characters in to test, which ends up as around 48 pixels. Thus the width of the entire display is 
36+38+48, ie 122 pixels. Centring this within 189 pixels gives an x-offset of around 34 for Press. 


Vertically, there are some 80-(4 +3*9)-4 pixels to play with, ie 45 pixels. With 8 pixels for the height of 
the text, this leaves 18 pixels clear above the top of the text, translating into a vertical offset of 
4+3*9+18+7 pixels to the baseline of the text, ie 56 pixels. 


This means that the code to display the middle line can be written as 


LOCAL_C VOID DisplayPressButton(VOID) 
¢ 
gPrintText(34,56,"Press",5); 
DrawButton( FALSE); 
gPrintText(34+36+38+6,56,"to test",7); 
> 


where Draw8utton (discussed below) draws the button itself, in either its normal or its depressed state 
(depending on the parameter passed to it). 


The above discussion again assumes a maximum screen width of 240 pixels which is valid for the Series 3 
and Workabout, or the Series 3a in Series 3 emulation mode. The Series 3a in native mode has a 
maximum screen width of 480 pixels. Therefore, to take advantage of the larger screen size of the 

Series 3a, the above calculations would need to be reworked. 


Positioning an action button vertically 


As for the vertical positioning of the button, bear in mind that it requires at least 6 pixels for its "edge 
effects" at top and bottom: 


= one pixel for the top line 

= one clear pixel underneath that 

= one pixel each for the two bottom lines 

= one clear pixel between the bottom lines, and one above the upper of these lines. 


For text in the system font, which has height 8 pixels, this means that the total height of the button 
should be at least 14 pixels. With a height of fourteen pixels, the baseline of the text comes 1+1+7 
pixels below the top of the box. 


Since this must match the baseline of the accompanying text Press and fo test, it follows that the top of 
the box should be at 56-9 pixels. 


This the code for DrawButton is 


LOCAL_C VOID DrawButton(INT state) 
€ 
P_RECT box; 


box. tl.x=34+36; 

box. br .x=34+36+38; 

box.tl.y=47; 

box. br. y=47+14; 
wDrawButton(&box, "Enter", state); 
> 


56 


2 WORKED EXAMPLES IN HWIF 
OO SeSSSSSSSSSSSSSSSSSSSSSSeSeSSS 


Animating the action button 


When the user does indeed press ENTER, the button should visibly depress, before any multiplication 
question is posed. The code to animate the button is as follows: 


LOCAL_C VOID MakeButtonDance(VOID) 
€ 
DrawButton( TRUE): 
wFlush(); 
p_sleep(2); 
DrawBut ton( FALSE); 
wFlush(); 
p_steep(2); 
> 


Suggestions for modifying Ta2 
= Display the top line in bold 


= Improve the above calculation so that it takes into account the fact that not all characters have 
widths of six pixels, and thereby position the test Press Enter to test yet more centrally 


= Consider how to incorporate displaying the value of state.Timing too 
= Experiment by removing the wrlush and/or the p_sleep from MakeButtonDance, to ensure that you 
understand their role in this routine. 
Presenting an edit box 


In Ta2, when the user presses ENTER, all that happens is that the score is incremented. In Ta3, the screen 
alters to the following form: 


Score: 4 correct out of 9 
Testing 7 times table 
Cup to ? times 12) 


Px12= 82] 


The left hand part is just the result of one more call to gPrintBoxText, for the text (in this case) 7x 12 =. 
The right hand part is an edit box for the user to enter the answer. There is a flashing cursor in the edit 
box. 


The logic of positioning the text display and the edit box is somewhat similar to that above for 
positioning the action button and its surrounding text. The logic for presenting the edit box itself is new. 


First, the edit box has to be created: 


LOCAL_C VOID *CreateEditor(VOID) 
{ 
H_EDIT_BOX heb; 


heb.maxchars=4; /* allow up to four characters to be typed */ 


heb. vulen=30; /* the width is 30 pixels (enough for 4 characters plus the cursor) */ 
heb. pos .x=113; 

heb.pos.y=45; 

heb.win=MainwWid; /* use the main (screen) window */ 

heb. font=WS_FONT_BASE; /* use the standard font */ 


heb.style=G_STY_DOUBLE; /* but with double height */ 
returnChEBOpen(H_EDIT_BOX_FONT,&heb)); 
> 


The meaning of the flag H_€01T_80x_FONT passed is that the font and style fields of the passed H_EDIT_BOX 
struct are significant. 


After creating the edit box, it has to be instructed to display its flashing cursor: 


hEBEmphasise(ebH, TRUE); 


57 


PROGRAMMING IN HWIF 


Next, all suitable keypresses have to be diverted in its direction: 


FOREVER 
€ 
uGetKey(&key); 
if (BadKey(key.keycode)) 
Beep(); 
else if (key. keycode! =W_KEY_RETURN) 
hEBHandl eKey(ebH , key. keycode, key.modi fiers); 
else 


When the user again presses ENTER, the contents of the edit box have to be sensed: 


LOCAL_C INT SenseNumberTyped(VOID *ebH) 
{ 
TEXT *pb; 
WORD num; 


pb=hEBSenseText(ebH); 
p_stoi(&pb, &num); 
return(num); 

a 


If the answer is as expected, the score is incremented, and the user is returned to the base state of the 
application. If the answer is incorrect, the user is given the opportunity either to retry, or to be told the 
correct answer. This interaction takes place via a couple of dialogs. If the user opts to retry, the last 
answer proffered is redisplayed, but completely highlighted so that any typing deletes it at once: 


LOCAL_C VOID SelectALLC(VOID *ebH) 
€ 
hEBSetSelect(ebH,0,p_slen(hEBSenseText(ebH))); 
> 


In all cases, when the application returns to its base state, the resources allocated for the edit box are 
freed by making the call 


hEBClose(ebH); 


Generating random numbers 
The multiplication questions are generated very easily: 


LOCAL_C INT FindRandomC(INT low, INT high) 
{ 
INT range; 


range=high-low+1; 
return( Low+(INT)(p_randl (&seed)%range) ); 
> 


LOCAL_C VOID FindFactors(WORD *pa,WORD *pb) 
€ 
*pa=(state.Mode==1? FindRandom(2,state.MaxTable): state.Mode); 
*pb=F indRandom(2, state. TableEnd); 
> 


The seed for the random variable generator is initialised by making the following call from specificinit: 
seed=p_date(); 


Further comments on Ta3 


Note the following sequence of calls, to print the multiplication question in double height: 


SwitchStyle(G_STY_DOUBLE); 
gPrintBoxText(&box, 15,G_TEXT_ALIGN_RIGHT,0,&buf [0] ,p_slen(&buf (01 )); 
SwitchStyle(G_STY_NORMAL); 


Switching the style back to normal again is clearly important, since otherwise, the next time any other 
call to gPrintBoxText is made, that text will end up in double height too. 


58 


2 WORKED EXAMPLES IN HWIF 
—_— ee SeSSeeeSSSSSSSSSSSSSSSSSSSSSsheFsesese 


When the editor is active, Ta3 enters a special inner mode, with another get-event loop. Access to the 
menu bar is ruled out until the user has responded to the question in hand. Since no attention is paid to 
Shutdown messages during this inner mode, the Epoc static patLocked is set TRUE before entering this 
loop, and is cleared again on exiting it. 


One difference between the edit box used in Ta3 and those used in dialogs is that the former has double 
height display, for special emphasis purposes. This is of course not possible in dialogs. Another 
important difference is that handling the interaction with the editor directly, as in Ta3, allows the whole 
interaction to be terminated when a timer expires - as happens in the next step up from Ta3, namely 
Tables itself. 


Suggestions for modifying Ta3 


= Improve the inner get-event loop to respond to Shutdown messages from the System Screen 


= Add another action button, with the text Press Enter to confirm, while the editor is displayed; to 
make room for this, change from using double height style to bold style 


= Replace the code handling the edit box and its associated text with some invoking a suitable 
dialog (albeit with single-height lines); note how simpler the code is in this case 


= Keep track of which questions the user answers incorrectly, and modify the code generating the 
questions so as to make these questions more likely to be asked again in the future. 
Adding in a timer 
When the user presses ENTER in the base state of Tables, the screen alters to the following: 


Score? 4 correct out of 18 pected 
Testing 7 times table 2x49 


Cup to 7 times 12) Tables 


?x12= 83] md 


Thu 23 


The edit box and its accompanying text have moved up, and a bar gauge has appeared. This is 
incremented as time passes, and users have to complete their answer before the bar fills completely. 


If the total time allowed is less than five seconds, the display updates once every half second; otherwise, 
it updates itself once a second. 


In Specificinit, a timer channel is opened, with the call 
P_open(&timH,"TIM:",-1); 


When an edit box is about to be displayed, the timer and some associated state variables are prepared for 
action by the routine 


LOCAL_C VOID InitialiseTimer(VOID) 


{ 

if (state. Timing<=4) 
{ 
timint=5; 
timcount=2*state.Timing; 
> 

else 
€ 
timint=10; 
timcount=state.Timing; 
} 

timtotent=timcount; 

QueueT imer(); 

} 


The variables timcount and timtotent are used in drawing the bar gauge. 


59 


PROGRAMMING IN HWIF 
_—_—_———  —  eeeFeFeSeSeSeSeSeeeeeeSSSSSSSSFFseseF 


Evidently, part of this preparation stage is to prime the timer: 


LOCAL_C VOID QueueTimer(VOID) 
¢ 
CancelTimer(); 
p_ioa4(timH,P_FRELATIVE,&timstat,&timint); 
timact ive=TRUE> 
> 


The reason why the routine queueTimer starts off with a call to cancetTimer is to cater for the case when 
the user retries an answer. 


CancelTimer protects itself against cancelling an event that does not exist, by means of checking the 
variable timactive: 


LOCAL_C VOID CancelTimer(VOID) 


C 

if (timactive) 
{ 
P_iow2(timH,P_FCANCEL); 
p_waitstat(&timstat); 
> 

timactive=FALSE; 

> 


As can be seen, timactive is set TRUE whenever any p_ioc call is made for the timer. It is set back to FALSE 
again, either inside Cancel Timer, or in the get-event loop, whenever the expiry of the timer is detected: 


p_iowait(); 

if (keystat==E€_FILE_PENDING) 
€ /* the timer must have expired */ 
timactive=FALSE; 
IncrementBarChart(); 


The routine CancelTimer is also called when exiting the inner get-event loop 


DatLocked=FALSE; 
hEBClose(ebH); 
ClearBottomsrea(); 
DisplayPressButton(); 
Cancel Timer(); 

> 


The call to p_waitstat inside CancelTimer is vital since, as for all the p_FCANCELS in Epoc, the timer 
P_FCANCEL does not stop the timer from completing (and thereby signalling). Rather, it precipitates the 
completion (if it has not already taken place). 


Drawing the bar gauge 
The outside of the gauge is drawn by a call to gBorderRect: 


box.tl.x=8; 

box. br .x=189-8; 

box.tl.y=61; 

box.br.y=61+8; 
gBorderRect(&box,W_BORD_CORNER_1); 


The grey pattern inside is drawn by calls to gFillPattern, using the built-in grey bitmap: 


LOCAL_C VOID IncrementBarChart(VOID) 
€ 
P_RECT box; 


timcount--; 

GetBarChartRect(&box); 

box. br.x=9+171*(timtotent-timcount)/timtotent; 
gFillPattern(&box,WS_BITMAP_GREY,G TRMODE_REPL); 
} 


60 


2 WORKED EXAMPLES IN HWIF 
—_— eee 


The rectangle returned by GetBarChartRect is the same as that used to draw the outside of the gauge, 
except that it is inset by one pixel all around. 


Limitation on debugging Tables 


Due to a limitation in some earlier versions of the Series 3 ROM, applications such as Tables, which use 
asynchronous keyboard reads, may find they are unexpectedly panicked with panic 73, while debugging. 
This can arise in the following situations: 


= the program has stopped at a break point when it has a keyboard read outstanding (ie the 
program has broken following a timer event), and a key is pressed on the Series 3 


= or, the program has stopped at a break point when a timer has been queued, and the timer 
expires when the program is broken. 


In either case, the panic will not be immediate, but will occur later as the result of a signal being mis- 
identified. (Another problem that can occur, for the same reason, is that the timer will never complete.) 
Suggestions for enhancing Tables 


® Allow access to the Timing menu command only when a suitable password is supplied; this 
password could be set (via another menu command) only by a "supervisor", and the “student”, 
without knowing the password, would be unable to alter the time allowed for each question 


= Currently, the Tested and Correct fields can become arbitrarily high; impose some kind of limit 


= Consider a mode in which questions are posed repeatedly, without the user needing to press 
ENTER between every question; ask up to n questions repeatedly, where 7 has been set in 
advance by the user 


= Currently, the timer is reset for each question; allow users to answer as many questions as 
possible during a total amount of time specified; give points for correct values and deduct points 
for incorrect answers. 


a a ee a Oe ah a a ee 
The remaining example applications 


Much could be said about the remaining example applications which cover a wide variety of different 
function calls and programming ideas. However, any readers who have managed to follow the discussion 
so far in this chapter will be well placed to unravel the contents of these other applications by themselves. 


One possible exception is the use of a resource file in the Remind example application (This feature was 
not present in earlier versions of this application). 
Resource file access with REMIND 


See the chapter on Resource Files in the Additional System Information manual for background 
information about the value and use of resource files generally. 


In Remind, all text has been removed from the source module remind.c and has been placed in suitable 
structures in remind.rss. Code in remind.c sees that these resources are loaded when needed. 


Several aspects of this should be noted: 


« The custom project file remind.pr contains the instruction "runrs remind" which has the result of 
creating the binary file remind.rsc from the input plain text file remind.rss using the batch file 
rs.bat which in turn invokes the resource compiler rcomp.exe 


= This project file runs the resource compiler UNCONDITIONALLY but a more sophisticated 
project file, as discussed in the Object Oriented Programming Guide, could avoid recompiling 
the resource file unnecessarily (assuming no changes have been made) 


= The binary file remind.rsc is listed in the add-file-list file remind.afl to ensure that it is 
automatically linked together with the object code as part of the application file remind.app 


= The routine LoadMenus in remind.c loads the menu text out of the resource file into static data 
structures AND THEN "walks" these data structures, converting them into the form required by 
the Hwif menu subsystem 


= Incontrast, string data is only loaded into memory when required using the function Loadstr 


61 


PROGRAMMING IN HWIF 
—_——— ee 


The code in remind.c is copiously commented. 


62 


CHAPTER 3 


ADVANCED USE OF HWIF 


This chapter describes how to build your own version of the Hwif library and hence how to add your 
own extensions to Hwif. It also explains, with examples, how to combine Hwif programming with the 
use of Psion's object oriented programming techniques. 


ESS ee ee ee ee 
Building the Hwif library 


Buildable source of the Hwif library is supplied as an HWIFSRC component on the Optional disk of the 
SIBO C SDK software. If installed, this source is copied into a \sibosdk\hwifsrc directory. 


This directory should contain all the source files necessary to build your own version of the Hwif library, 
but you may need to insure that the ts.red file is suitable for your environment. Since the Hwif source 
code contains some object oriented software, you will also need to install the OOP component from the 
Optional disk before building the library. 


Executing the make.bat batch file in the \sibosdk\hwifsrc directory will create an hwif.lib file that should 
be identical, apart from four bytes of date-stamp information, to the hwif.lib that is copied into the 
\sibosdk\lib directory by installing the HWIF component from the Optional disk. Note that making the 
Hwif library will also create an hwifo.lib library, whose use is described later in this chapter. 
Extending Hwif 


Once you have successfully built an Awif.lib that reproduces the one supplied with the SDK software, 
you may, if you wish, add your own extensions. These will typically be additional utility functions, but 
could be anything that you wish to add to Hwif. 


All you have to do is add further code, either to the existing Hwif source files, or to additional source 
files and then rebuild the library. You may, if you wish, modify the make. bat file to remove the line: 


tscx /m hwifo /v0 


so that the Awifo. lib library is not rebuilt. If you have written source code in additional files you will, of 
course, have to modify the Awif.pr project file to include them. 


a a ek SY Re ee 
Combining Hwif with object oriented code 


It is possible for an Hwif program to be written to use parts of the built-in object oriented libraries OLIB, 
FORM, HWIM and (on the Series 3a and Workabout) XADD. One of the most important advantages of 
doing this is to use one or more object oriented (HWIM) dialogs, via the hoodialog utility function. 
Because of its importance, the rest of this chapter concentrates on the techniques that allow the use of this 
function. 


HWIM dialogs support several features not available to Hwif dialogs. For example: 
= Subdialogs can be launched when the user presses Tab. 


= The value shown in one field can be made to change dynamically according to changes made by 
users in other fields in the dialog. 


= Features such as locking items or dimming items are also available. 


63 


PROGRAMMING IN HWIF 


See the Object Oriented Programming Guide and the HWIM Reference manual for a full discussion of 
programming HWIM dialogs. The following description assumes some familiarity with the contents of 
those manuals. 


In the Psion programming system, the class definition of an object oriented class must appear in a 
category file, and each code segment may only be associated with a single category file. Since the Hwif 
library is associated with its own category file, this means that application-specific classes can not simply 
be added directly to an Hwif program. 


When combining application-specific classes with an Hwif program, the classes can be defined: 
= ina separate category file, associated with a separate (DYL) code segment; 
=# ina modified Hwif category file. 


These two alternatives are described in the following two sections. 


Using a separate DYL 


This technique is illustrated by the OQuery example application that may be copied into a 
\sibosdk\hwifood directory by installing the HWIFOOD component of the Optional disk. This example 
will be recognised as a version of the standard Hwif Query example program. 


In OQuery, only one dialog is converted into HWIM form; this is the dialog to scan forwards or 
backwards in time to find the next occurrence of a particular date combination. On running the 
application, the difference between this and the original Hwif dialog (in particular, its "flicker free" 
updating) should be immediately noticeable. 


In addition to the changes needed to run an HWIM dialog, all use of the floating point emulator has been 
abolished by replacing explicit floating point manipulation with calls to the p_fxxx functions. 


The resource file, oquery.rss, contains the following dialog resource: 


RESOURCE DIALOG oqd_date_combins 
€ 
title="Find date combinations"; 
controls= 
€ 
CONTROL 
€ 
class=C_CHLIST; 
prompt="Day in week"; 
info=CHLIST { rid=oqm_daynames; }; 
>, 
CONTROL 
€ 
class=C_NCEDIT; 
prompt="Day in month"; 
info=NCEDIT 
€ 
low=1; 
high=31; 
); 
}, 
CONTROL 
€ 
class=C_DTEDIT; 
prompt="Found date"; 
info=DTEDIT 
€ 
flags=IN_DTEDIT_DDMMYYYY | IN_DTEDIT_INIT; 
low=0; 
high=93501L; 
3 
3, 


3 ADVANCED USE OF HWIF 
—_—_— SS eee 


CONTROL 
{ 
class=C_ACLIST; 
info=ACLIST € rid=oqa_date_combins; }; 
> 
3; 
> 


The code associated with the HWIM dialog itself is in the two files ogd.cat (the class definition of the 
dialog) and oqdc.c (the source code for the dialog's method functions). These are the only two source 
files that are used to build the application's DYL, ogd.dyl, using the techniques explained in the Object 
Oriented Programming Guide. 


The category file, ogd.cat contains the following definition of the oap_comBINs class that subclasses the 
HWIM btesox class: 


LIBRARY ogd 


EXTERNAL olib 
EXTERNAL hwim 


INCLUDE dlgbox.g 


CLASS oqd_combins dlgbox 
€ 
REPLACE dl_dyn_init 
REPLACE dl_key 
} 


which means that the class number of the dialog will be represented by the symbolic constant 
C_OQD_COMBINS. 


The name of the DYL is included in the DYL file list in oquery.dfl, which means that the DYL will be 
built into the final image file, oquery.img. This is the preferred way of packaging a DYL with an 
application. 


The code associated with running this dialog is in oquery.c. In the function specificinit¢) the DYL is 
loaded and its handle written to the static variable py|Handle by: 


DylHandle=hLoadOwnDyl (0); 


where the zero parameter indicates that ogd.dyl is the first (and, in this case, the only) DYL built into the 
.img file. 


The dialog is run, from the ManageCommand() function, by: 
hOODjalog(Dyl Handle, C_OQD_COMBINS,OQD_DATE_COMBINS, NULL); 


Note that static data is not allowed in a DYL,; all data transfer between an Hwif program and an HWIM 
dialog has to be via: 


= the rbuf result buffer; 


= the EPOC magic statics DatApp1 through DatApp7, which are specifically designed for this kind of 
use, 


In this application, there is no transfer of data to or from the dialog, and so the rbuf parameter is set to 
NULL. 


Also note that, in this example application, the DYL is never unloaded by application code. In general, a 
DYL should be unloaded (using the function p_unloadl ib) as soon as the code it contains is no longer 
required. 


Debugging an Hwif DYL 


Note that, when using the SIBO Debugger, you can only apply breakpoints in a code segment (or see its 
source code) when the code segment is loaded. 


If you wish to apply a breakpoint in, or otherwise debug, a DYL used by an Hwif program you must 
first run the program until the DYL is loaded. In the case of the OQuery program you could, for 
example, set a breakpoint on the line: 


DylHandle=hLoadOwnDyl (0); 


65 


PROGRAMMING IN HWIF 
_ — EeSeSSSSSSSSSSSSSSSSSSSMSSSSSee 


On stepping through this line, the DYL is loaded and you can then debug it as normal. 


You can, for example, view its code by using the Source module option of the Debugger's View menu 
and selecting the appropriate code segment and source file in the resulting dialog. 


You can set a breakpoint in the DYL without first viewing its code, provided you specify the code 
segment name (which is the same as the name of the DYL). Suppose you wish to set a breakpoint in 
oqd.dyl, on the dl_dyn_init method of the oap_come1ns dialog. While the DYL is loaded, you can, in the 
Debugger's Set breakpoints dialog, add the breakpoint by typing in: 


GQD:oqd_combins_dl_dyn_init 


Modifying the Hwif category 


The second way of introducing applicaton-specific object classes into an Hwif application is by 
incorporating them into the Hwif category file. Effectively, this is the same idea as described earlier to 
add extensions to Hwif. 


In principle, the way to do this is to: 
= add the application-specific class definitions to the end of the Hwif category file, hwif.cat, 
= write the additional code in one or more additional source files, 
= compile and link all the source files, including those of Hwif, into the application .img file. 


In practice, it is more convenient to do this in a different, but totally equivalent, way. Instead of 
modifying the Hwif category file, you create an application-specific category file. The initial lines of this 
file must be an exact copy of the Hwif category file, hwif.cat, whose content is shown below. 


IMAGE hwif 


EXTERNAL olib 
EXTERNAL hwim 


INCLUDE Lprinter.g 
INCLUDE help.g 


CLASS hprinter lprinter 

€ 

REPLACE lpr_read 

REPLACE ipr_sense_text 

PROPERTY 
€ 
INT (*sense)(WDR_PRINT *); 
> 

> 


CLASS hhelpdlg helpdlg 
{ 
REPLACE destroy 
} 


You may change the file name and the name in the IMAGE statement to match the particular application. 
Note that the two names must be the same, so that, for example, a category file called myapp.cat must 
start with the statement 


IMAGE myapp 


You may also, if necessary for the application, add further exTERNAL and/or header file INCLUDE 
statements. 


Apart from these possible changes and additions, the first part of the file must match the contents of the 
hwif.cat file exactly. This data should then be followed by the application-specific class definitions. 


Further files contain the application source code exactly as for a normal Hwif application, except that 
they also include the method function code for the application-specific classes. 


After compiling these files, you should link them with the Awifo. lib library file, rather than the normal 
hwif. lib. 


66 


3 ADVANCED USE OF HWIF 


The file hwifo.lib is copied into the \sibosdk\lib directory when you install the HWIF component from 
the Optional disk. It can also be built from the Hwif source code, as described earlier in this chapter. It 
differs from hwif.lib only in that it does not contain the Hwif category data. 


The technique of modifying the Hwif category file is illustrated by the gbar.img example code that is 
described in the following section. 


Sa SS a eS Se EES 


Access to a growing scroll bar from Hwif 


The source code for the gbar.img example application is copied into the \sibosdk\hwifood directory by 
installing the HWIFOOD component from the Optional disk. 


This example, in addition to illustrating the technique of including object oriented code by modifying the 
Hwif category, also provides an example of how to use a growing scroll bar, or percentage done 
indicator, in Hwif. 


Note that including a growing scroll bar in a dialog is only possible when the dialog is fully object 
oriented, that is, either in an HWIM application, or in a dialog called from Hwif via hooDiatog¢). 
To make gbar.img, run the makegbar.bat batch file in \sibosdk\hwifood. 


To run it, copy it to a top-level \IMG\ directory and run from under RunImg. When finished, press 
Psion-Esc to exit it. 


The first dialog in the loop lets you specify the parameters for how the second, growbar, dialog operates. 


The application's category file 
The category file, gbar.cat is as follows: 


IMAGE gbar 


EXTERNAL olib 
EXTERNAL hwim 


INCLUDE lprinter.g 
INCLUDE help.g 
INCLUDE dlgbox.g 


CLASS hprinter (printer 
4 
REPLACE lpr_read 
REPLACE lLpr_sense_text 


PROPERTY 
{ 
INT (*sense)(WDR_PRINT *): 
> 

> 


CLASS hhelpdlg helpdlg 
{ 


REPLACE destroy 
> 


67 


PROGRAMMING IN HWIF 
—_ TS SSeS 


CLASS data_dl dlgbox 
{ 
REPLACE dl_dyn_init 
REPLACE dl_key 
TYPES 
€ 
typedef struct 
€ 
UWORD exit_code; 
UWORD totloops; 
UWORD update; 
UWORD esc; 
UWORD loopsleft; 
UWORD toupdate; 
} RB_GBAR; 


> 


CLASS gbar_dl dlgbox 
€ 
REPLACE wn_sense_help 
REPLACE dl_dyn_init 
REPLACE dl_key 
ADD gbar_update 
> 


CLASS bar_ao active 
€ 
REPLACE ao_init 
REPLACE ao_run 


PROPERTY 
{ 
RB_GBAR “prb; 
> 

> 


Comparing this with Awif.cat shows that: 
= the IMAGE name has changed, 
= there is the additional inclusion of digbox.g 


= there are application-specific class definitions for the two dialogs DATA_DL and GBAR_DL, and the 
BAR_AO active onject. 


This application uses a pointer to a result buffer (in this case, an RB_GBAR struct in the property of the 
BAR_AO active object) to communicate with the dialogs. This is a design decision that is mentioned later. 


C_DONEWN items in dialogs 


As shown by the pt_sar dialog resource in gbar.rss, a growing scroll bar item in a dialog is defined in 
the resource file simply as: 


CONTROL 
€ 
class=C_DONEWN; 
> 


Compare this with, for example, the definition of a numeric editor control: 


CONTROL 
€ 
class=C_NCEDIT; 
prompt="Total number of loops"; 
jinfo=NCEDIT 
€ 
high=100; 
low=1; 
5 


68 


3 ADVANCED USE OF HWIF 
—_ TS SSeS 


The differences are: 
= there is no associated prompt 
m there is no info data. 


When a dialog contains a C_DoNewNn element, that element must be sent a WN_SET message, defining the 
range of the element. This must be done during the initialisation of the dialog, before it becomes visible, 
usually from the dl_dyn_init method of the dialog. For example, in the code of growbar.c: 


METHOD VOID gbar_dl_dl_dyn_init(PR_DLGBOX “self) 
€ 
RB_GBAR *prbuf; 
SE_DONEWN set; 


prbuf=sel f->dl gbox.rbuf; 
set.flags=SE_DONEWN_RANGE; 
set.range=prbuf->tot loops; 
hDlgSet(1,&set); 

} 


Note that the wN_SET message to the c_DONEWN element (the element with index 1 in the dialog) is hidden in 
the HWIM utility function hpigset¢). 


The SE_DONEWN struct and the WN_SET method of DONEWN 


The above code uses an SE_DONEWN struct to pass information in the wN_SET message. This struct is defined 
as follows: 


typedef struct 
€ 
UWORD flags; 
ULONG val; 
ULONG range; 
> SE_DONEWN; 


The value of flags can be any one of the following: 


SE_DONEWN_RANGE (0x01) set the control's range 
SE_DONEWN_VALUE (0x02) set the current value to val 
SE_DONEWN_INCREMENT (0x04) increase value by 1 
SE_DONEWN_INC_VAL (0x08) increase value by val 


For example, inside the gbar_update method of the eBar_pL dialog in growbar.c it is used to set a specific 
current value: 


METHOD VOID gbar_di_gbar_update(PR_DLGBOX *self) 
€ 
RB_GBAR *prbuf; 
SE_DONEWN set; 


prbuf=sel f->dl gbox.rbuf; 

set. flags=SE_DONEWN_VALUE; 
set.val=prbuf->totloops-prbuf->Lloopsleft; 
hDlgSet(1,&set); 

> 


Note that the above gbar_update method is not a REPLACEd method but one that has been oped by the 
definition of GBAR_DL to the set defined by the superclass pLGBox. 
The BAR_AO active object 


A typical use of a grow bar dialog is to report on the progress of an extended activity. The "update" 
method of the dialog has to be called every so often, during the course of the activity being described in 
the dialog. 


However, this activity has to take place in between the dialog starting and the dialog exiting and 
therefore has to take place inside the ao_run method of an active object. 


The example uses the BAR_AO active object which is created and initialised early in the Maintoop() function 
in growbar.c by the code: 


69 


PROGRAMMING IN HWIF 
_ ee SSS 


InitGbarData(&gb_ data); 


ao=f_newsend(CAT_GBAR_GBAR,C_BAR_AO,O_AO_INIT,&gb data); 
which calls the object's ao_init method: 


METHOD VOID bar_ao_ao_init(PR_BAR_AO *self,RB_GBAR *prb) 
{ 
p_send3(w_am,O_AM_ADD_TASK,self); 
sel f->bar_ao.prb=prb; 
> 


In the example, the activity is simulated in the ao_run method by simply calling p_sleep(5) to pause the 
application for half a second. The code for the method is: 


METHOD INT bar_ao_ao_run(PR_BAR_AO *self) 
¢€ 
RB_GBAR *prb; 


p_sleep(5); /* the next stage of computation or drawing etc */ 
p_send2(sel f ,O_AO_QUEUE); 
prb=sel f->bar_ao.prb; 
if (!--(Cprb->loopsleft)) 
{ 
p_send2(DatDialogPtr,0_DESTROY); 
prb->exit_code=0; 
} 
else if (!--(prb->toupdate)) 
{€ 
p_send2(DatDialogPtr,O_GBAR_UPDATE); 
prb->toupdate=prb->update; 
> 
return(RUN_ACTIVE_USED); 
3 


Note the following points about the code of this method: 


= The next stage of the "computation" is, for convenience, always queued in the ao_run method 
(by sending an Ao_queUE message, which will cause the ao_run method to be called again at some 
time in the future). 


= Inconsequence, the grow bar dialog itself can be called by the code: 


p_send2(ao,0_AO_QUEUE); 
doDial(C_GBAR_DL,DL_GBAR,&gb data); 
p_send2(a0,0_AO CANCEL); 


which always cancels the activity of the active object when the dialog completes. 
= The handle of the current dialog is always accessible from the “reserved static" patDialogpPtr. 


= — If the computation is finished, the active object sends a DEsTRoY message to the dialog. 
Otherwise, every so often, the active object sends an "update" message to the dialog. 


® Don't forget to 
return(RUN_ACTIVE_USED) 
from your ao_run method (on pain of being panicked 143, most likely). 
= Ip, at aoe there are no parameters to the “update” message, but in another example there 
might be. 


Termination of the grow bar dialog 


In general, the grow bar dialog can terminate in either of two ways: 


= the user presses Esc - in which case the call to hoopialog returns without the cooperation of any 
of the code in the ao_run method 


= the computation finishes - in which case the ao_run method sends the dialog a DESTROY message 
and this precipitates the completion of the hooDialog call. 


70 


3 ADVANCED USE OF HWIF 
eee 


General comments 


Help has to be disallowed while the grow bar dialog is running, to avoid accidents if the dialog is 
terminated before the help system is shut down. (These complications only arise for hooDjalog, and not 
for pure HWIM programs.) Hence the "magic" in the dialog's wn_sense_help method: 


METHOD VOID gbar_dl_wn_sense_help(VOID *self) 
{ 
p_leave(RUN_ACTIVE_USED); 
> 


For a similar reason, access to the freeform dialler has to be disallowed - hence the "magic" in the 
routine DisallowDial ling: 


LOCAL_C VOID DisallowDiall ing¢VOID) 
€ 
W_ws->wserv. flags |=PR_WSERV_FREEFORM_DIALLING; 
> 


For simplicity, the code deliberately ignores various run-time errors that might arise - for example, 
running out of memory when launching either dialog. 


In growbar.c, the active object is created early in the application, and is used repeatedly each time the 
grow bar dialog is invoked. Another design approach would be to have the active object exist only 
throughout the lifetime of an individual grow bar dialog. 


Another design decision in this example is not to access any specific static data from inside either the 
dialog code or the active object code. Each of these object interacts with the rest of the world only via 
the result buffer pointers, as noted earlier. An alternative design option would be to access more static 
data from inside these objects. 


71 


CHAPTER 4 


HwiF REFERENCE DOCUMENTATION 


SS eS eee oe re ey 
Overview of the Hwif library 
Routines in the Hwif library fall into two categories: 

= utility functions, which have names starting with lower-case u 

= primitive functions, which have names starting with lower-case h. 


The former category are routines which an experienced Hwif programmer could dispense with or re- 
write. They layer over Window Server function calls, Console I/O requests, and some of the more 
primitive Hwif routines. They turn out to be very useful in practice, but if the need arises, they can in 
principle be replaced by alternative code. 


On the other hand, the #-routines can be replaced only by someone familiar with Psion’s proprietary 
object-oriented system. 

Two levels within the h-layer calls 

In turn, functions in the h-layer of Hwif can be further classified: 


= Low level functions, which would normally be accessed directly only by programmers providing 
their own versions of the u-level functions 


« High level functions, which are more widely useful - such as hPrint, hEBSenseText, and 
hDTMFString. 
Two layers within the u-layer calls 
The u-layer calls in the Hwif library can also be classified into two types: 


= Central functions, which are likely to be called in every non-trivial Hwif application, and which 
encapsulate detailed knowledge of the operation of the low level h-layer Hwif functions 


= Auxiliary functions, whose contents are more straightforward, and which can more easily be 
duplicated by applications programmers. 


Many of the auxiliary functions in fact exist in the library only because they are called from within other 
Hwif functions. It would be wasteful for applications to create their own versions of these functions, 
since this would lead to two duplicate functions in the same application. 


Groups of functions and naming conventions 


The following groups of high-layer h-routines each have their own naming convention, to clarify their 
roles: 


hEBxxx edit box functions 

HT TXxx time text functions 

hDT xxx date/time text editor functions 
hPrintxxx Printing and print support functions. 


73 


PROGRAMMING IN HWIF 


Return values and error notification 


In most cases, Hwif functions that can fail return negative values to indicate the nature of the failure, and 
zero or a positive value to indicate success. (The exceptions include a few constructive functions, which 
return the handle of some allocated block if successful, and NuLL if not.) 


For a function which can only have two outcomes, success or failure, the former is usually indicated by 
the return value 0, and the latter by the return value -1. In practice, the outcome can be determined by 
the caller making a simple test, such as 


if (TryOpenDbf(...)) 

ae /* the call to TryOpenDbf failed */ 
else 

sto. /* the call succeeded */ 


or 


if (uOpenDialog(NULL)) 
return; /* no memory to open the dialog */ 


Most Hwif library routines automatically notify the user of any error that arises, before returning the 
error value to the caller. It is only the low level h-layer routines that leave the notification to the caller. 


Binary counted strings 


Although most of the Series 3 ROM functions work with ZTSs (zero terminated strings) as opposed to 
BCSs (leading byte-counted strings), some of the functions within Hwif instead work with BCSs. 


Where conversion between the two types of representation of string is required, this is obviously 
straightforward. 


In all cases below, strings are assumed to be given in ZTS form, unless otherwise stated. 


Dialog and menu interactions as a special mode 


An Hwif application enters a special mode when it makes the calls uPresentMenus, uRunDialog, hPrint or 
hPrintSetupDialog: 


= messages from the System Screen are blocked during this time, with the user being told that the 
application is "busy" 


= if, during a menu or dialog interaction, the application is tasked into background or foreground, 
or is switched off then on again, the application is not notified of this fact 


= if a timer expires, or another non-keypress event occurs, the application only finds out about this 
when the user in due course concludes the menu or dialog interaction. 


In the above cases, the processing of events passes temporarily out of the hands of application, to a 
central get-event loop in ROM code. This ROM get-event loop can only process events from event 
sources it explicitly knows about - and this excludes any timers, alarms, or other non-keypress event 
sources installed by the application. If the ROM get-event loop detects a signal without the status words 
of any of the event sources it knows about being written to (as will occur if, for example, an application- 
installed timer expires), it simply increments an internal counter. When program execution is about to 
pass back out of the ROM get-event loop to the application, one signal is re-emitted for every time the 
internal counter was incremented. 


a a a a eS et RR OR a ee 
The central functions in the u-layer of Hwif 


i to Hwif applications 
VOID uCommonInit(VOID); 


Opens and sizes a console window appropriate for the Series 3, Series 3a or Workabout screen. Initialises 
control blocks for subsequent menu and dialog interactions. 


If unsuccessful (for example, because the Window Server has insufficient memory to open the console 
window), the application is terminated with an in-line call to p_exit. 


74 


4 HWIF REFERENCE DOCUMENTATION 
SSS 


If successful, the address of the control block of the console is written to the globally referenced static 
VOID *winHandle; 


The routine copes with the case when wintandle is non-zero when the routine is called - usually because a 
CLIB start-up module has been used. Rather than attempt to open the console again, the existing console 
channel is used, with any flashing block cursor being turned off. 


A basic example of the use of uCommontinit is: 


GLDEF_C VOID main(VOID) 
{ 
uCommonInit(); 
SpecificInit(); 
MainLoop(); 
> 


The general behaviour and appearance of the console window that is created in a call to ucommoninit 
depends on the value of the global variables _UseFul screen and _S3UseFul lScreen on entry to the call. 


On all machines, if the values of _useFulltScreen and _S3UseFul Screen are zero (the default values) Hwif 
is initialised in Series 3 compatibility mode. This means that the behaviour and appearance of the console 
on the Series 3a or Workabour emulates the behaviour of the console on the Series 3. 


If, however, the value of Useful Screen is non_zero, Hwif is initialised appropriately for the machine on 
which the application is running. On the Series 3a and Workabout, the application can take full 
advantage of the larger screen size, grey and other additional features. 


The value of _s3UseFul screen is ignored by Series 3 and Series 3a machines. On the Workabout, if 
_S3UseFul (Screen is non-zero (and regardless of the value of _UseFut tscreen) Hwif is initialised to use the 
Window Server's w_ctTeY_s3_scr compatibility mode. This is a Series 3 (i.e. no grey) compatibilty mode, 
but the full 240x100 area of the Workabout screen is used. For example: 


GLREF_D UWORD _S3UseFul lScreen; 


GLDEF_C VOID main(VOID) 
€ 
_S3UseFul lScreen=TRUE; /* Set Workabout to $3 full-screen compatibility */ 
uCommonInit(); 


} 


Note that on all machines (except the Workabout in true Series 3 compatibilty mode, where a 240x80 
window is used) the console window is created to be occupy the full screen. Regardless of its size, it will 
always display an exact number of lines of text. 


In addition to its use to specify whether or not to use compatibility mode, the value of _UseFul LScreen can 
be tested on return from uCommontinit to determine the type of the machine on which the code is running; 
a non-zero value means that the application is running on a Series 3a or Workabout, while a zero value 
means that the machine is running on the Series 3. This is demonstrated in the following code fragment: 


GLREF_D UWORD _UseFullScreen; 


GLDEF_C VOID main(VOID) 
{ 
_UseFul LScreen=TRUE; 
uCommonInit(); 
if (_UseFul l|Screen) 
€ 
/* Running on a Series 3a or Workabout */ 


> 
else 
€ 
/* Running on a Series 3. */ 


} 


A call to uCommontnit does not write anything to _s3UseFul Screen so there is no equivalent test that can 
be made on its value. 


75 


PROGRAMMING IN HWIF 


VOID uEnableGrey(VOID); 


This function enables the use of grey in the main console window. It must be called before any attempt is 
made to draw grey on the Series 3a or Workabout. 


Because of the overhead involved, grey should not be enabled as a matter of routine. If the application 
never intends to draw grey then it should not be enabled! 


Ideally, it should be called as soon as possible after the call to uCommoninit. In general, a call to this 
function should be imbedded in the initialisation code specific to the application. Referring to the general 
structure of an Hwif program as mentioned in the description of uCommoninit, the function specificinit¢) 
is usually a good place to imbed a call to uEnableGrey. 


The following program is a very simple example that draws a shadowed grey effect border using the 
function gBorder2, if running on the Series 3a or the Workabour: if running on the Series 3, or on the 
Series 3a or Workabout in compatibility mode, it draws a simple shadowed border without grey. 


On the Series 3a and Workabout, grey must be enabled before the grey shadowed effect border can be 
drawn. 


#include <p_std.h> 
#include <wlib.h> 
#include <hwif.h> 


GLREF_D UWORD _UseFullScreen; 


LOCAL_D WORD keystat; 
LOCAL_D INT gc; 


LOCAL _C VOID SpecificInit¢(VOID) 
{ 
gc = gCreateGCO(uF indMainWid()); 
if (_UseFul lScreen) 
€ 
wFree(gc); 
uEnabl eGrey(); 
gc = gCreateGCO(uF indMainWid()); 
gBorder2(W_BORDER_TYPE_1,W_BORD_CORNER_4|W_BORD_SHADOW_ON|W_BORD_SHADOW_D); 
} 
else 
gBorder(W_BORD_CORNER_4|W_BORD_SHADOW_ON|W_BORD_SHADOW D); 
> 


LOCAL_C VOID MainLoop(VOID) 
€ 
WMSG_KEY key; 


FOREVER 
€ 
uGetKeyA(&keystat, &key); 
p_iowait(); 
if (key.keycode & W_SPECIAL_KEY) 
if (Ckey.keycode & (“W_SPECIAL_KEY)) == 'x') 
p_exit (0); 


> 


GLDEF_C VOID main¢VOID) 
€ 
_UseFullScreen = TRUE; 
uCommonI nit(); 
Specificinit(); 
MainLoop(); 
> 


It is important to note that uEnableGrey causes the ID of the main window to change. Thus, after calling 
uEnableGrey and before calling any functions that need the main window ID as a parameter, call 
uF indMainwWid. 


76 


4 HWIF REFERENCE DOCUMENTATION 


VOID uGetKey(WMSG_KEY *pkey); 
Reads a keypress into *pkey, waiting indefinitely if no keypress is received. 


The routine also returns if any special event is received; in this case, the keycode has the bit W_EVENT_KEY 
set. The various possible values in this case are given in the header file p_cons.h: 


CONS_EVENT_FOREGROUND The application has passed into foreground 
CONS_EVENT_BACKGROUND The application has passed into background 
CONS_EVENT_ON_OFF The machine has been switched off and then on again 
CONS_EVENT_COMMAND The application has received a message (probably from the 


System Screen) and should call weetCommand to obtain a buffer 
containing more details. 


CONS_EVENT_DATE_CHANGED The system date has changed. Typically, the application will get 
this message when the system date passes midnight. 


Note that this is only available on Epoc V3.18 or later and 
Window Server V4.32 or later 


For example: 


LOCAL_C VOID MainLoop(VOID) 
.¢ 
WMSG_KEY key; 


FOREVER 
< 
uGetKey(&key); 
if (key. keycode&W EVENT KEY) 
{ 
if (key.keycode==CONS EVENT_COMMAND ) 
ProcessSystemCommand( ); 


VOID uGetKeyA(WORD *pstat,WMSG_KEY *pkey); 


Reads a keypress into *pkey whenever the next keypress is received, without however waiting for this to 
occur. The value of *pstat is changed to E_FILE_PENDING when the call is made. When a keypress is 
received, the value of *pstat is changed from E_FILE_PENDING to 0. 


An application that calls uGetkeyA when the previous such call is still outstanding is liable to be panicked 
in due course with panic 73. 


77 


PROGRAMMING IN HWIF 
Ee 


For example: 


LOCAL_D WORD timstat; 
LOCAL_D WORD keystat; 
LOCAL_D WORD keyactive=FALSE; 


LOCAL_C VOID MainLoop(VOID) 
€ 
WMSG_KEY key; 


FOREVER 
{ 
if (keyactive) 
wFlush(); /* flush any outstanding graphics calls */ 
else 
{ 
uGetKeyA(&keystat, &key); 
keyact ive=TRUE; 


> 
p_iowait(); /* wait for something to happen */ 
if (keystat==E_FILE_ PENDING) 

{ 

owe /* the timer must have expired */ 
else 

{ 

keyact i ve=FALSE; 

eine /* proceed as above */ 

> 


VOID uCancelGetKeyACVOID); 


This function cancels any outstanding asynchronous request for a keypress or any of the other event types 
described in uGetKey. 


Consider the code fragment given as an example in the description of uGetkeyA. This could be modified to 
include a call to uCancelGetkeyA as soon as the timer has expired as shown below: 


78 


4 HWIF REFERENCE DOCUMENTATION 
—_ ee eS 


LOCAL_D WORD timstat; 
LOCAL_D WORD keystat; 
LOCAL_D WORD keyactive=FALSE; 


LOCAL_C VOID MainLoop(VOID) 
£ 
WMSG_KEY key; 


FOREVER 
{ 
if (keyactive) 
wWFlush(); /* flush any outstanding graphics calls */ 
else 
{ 
uGetKeyA(&keystat ,&key); 
keyact i ve=TRUE; 
} 
p_iowait(); /* wait for something to happen */ 
if (keystat==E_FILE_ PENDING) 
{ 
ese /* the timer must have expired */ 


uCancelGetKeyA(); /* cancel outstanding keypress requests */ 
keyactive=FALSE; /* no key press requests outstanding */ 


> 
else 
{ 
keyactive=FALSE; 
Ane /* proceed as above */ 
> 


INT uKeyPressOutstanding(VOID); 


Returns TRUE if a keypress is outstanding, else FALSE. 


For example: 


UpdatePending=FALSE; 
FOREVER 
{ 
uGetKey(&key) ; 
switch (key. keycode) 
{ 
ee /* may set UpdatePending */ 
> 
if (UpdatePending && !uKeyPressOutstanding()) 
€ 
Drawlcon(); /* time consuming */ 
UpdatePending=FALSE; 
> 
> 


Note that for the purposes of this routine, "keypress" does not include a general console event (such as 
coming into foreground). 


INT uLocateCommand(INT accel); 


Looks through the table of commands implicitly identified by the static _cmds, searching for a command 
with the accelerator accel. 


Retums -1 if no match is found, or else the index of the matching command, starting with 0 for the first 
command. 


79 


PROGRAMMING IN HWIF 
—_—————. $e 


For the Series 3, the sets of valid accelerators are: 
= the lower case letters ‘a’ to 'z' inclusive. 
= four characters that vary from language to language - in English they are '+", '-', '*' and '/'. 


For the Series 3a and Workabout, the sets of valid accelerators are those which are valid for the Series 3 
plus: 


= the upper case letters 'A’ to 'Z' inclusive. 


The Series 3a and the Workabout distinguish between shifted and unshifted alphabetic accelerator keys. 
For example, PSION+A and PSION+SHIFT+A may be used to invoke two different commands. 


Shifted accelerators are not available on the Series 3 and should not be used in software intended to run 
on any range of machine types that includes the Series 3. 


A shifted accelerator is defined by an upper case accelerator in the command array, as for the Search 
backwards command in the following example: 


LOCAL_D TEXT *cmds[]= 
€ 
"mNew File", 
"aSaveas", 
“sSearch forwards", 
"SSearch backwards", 
"XExit", 
NULL 
3; 


Before calling uLocateCommand, a small change must be made to the accelerator key handling code; if the 
Shift Modifier is set, the accelerator key must be converted to uppercase. This is illustrated in the 
following code fragment: 


LOCAL_C VOID TryExecuteCommand(INT keycode) 
€ 
INT comid; 


comid = uLocateCommand(keycode); 
if (keycode >= 0) 
{ 
/* execute command with command ID comid */ 


LOCAL_C VOID MainLoop(VOID> 
€ 
INT code; 
INT ret; 
WMSG_KEY key; 


80 


4 HWIF REFERENCE DOCUMENTATION 
i er ee ee i ee 


FOREVER 
€ 
uGetKey(&key); 
if (key. keycode & W_SPECIAL_KEY) 
€ 
code = key.keycode & (“W_SPECIAL_KEY); 
if (key.modifiers & W_SHIFT_MODIFIER) 
code = p_toupper(code); /* code change needed to use shifted accelerators */ 
TryExecuteCommand(code); 
3 
else 
{ 
switch (key.keycode) 
€ 
case W_KEY_MENU : 
if (key.modifier & W_CTRL_MODIFIER) 
€ 
/* toggle status window */ 
else 
€ 
ret = uPresentMenus(); /* no code change here */ 
if (ret > 0) 
TryExecuteCommand(ret) 
> 
break; 
case W_KEY_TAB : 
break; 
case W_KEY_RETURN : 
break: 
> 
> 
> 
3 


Note that no change is required to the code concerned with selecting a command by highlighting its menu 
item and pressing Enter (implemented by a call to uPresentMenus). 


INT uPresentMenus(VOID); 


Commence a menu bar interaction, presenting the menus implicitly defined via the statics _cmds and 
_mdata. Waits until the menu interaction has terminated before returning. 


This function returns: 


= 0 if the user cancels or if an error such as out of memory (OOM) occurs - in which case the user 
will already have been notified of this 


= the accelerator of the command chosen. 
The Epoc static DatLocked is set TRUE for the duration of the call to uPresentMenus. 
The call requires Window Server resources, and hence its success can never be guaranteed. 
For example: 


LOCAL_C VOID TryExecuteCommand(INT keycode) 
€ 
keycode=uLocateCommand( keycode) ; 
if (keycode>=0) 
ManageCommand(keycode); 
> 


81 


PROGRAMMING IN HWIF 
————_— SSSSSSSSSSSSSSSSSSFMMFee 


LOCAL_C VOID MainLoop(VOID) 


€ 
INT ret; 
FOREVER 
€ 
uGetKey(&key); 
if (key. keycode&W_SPECIAL_KEY) 
TryExecuteCommand( key. keycode&(~W_SPECIAL_KEY)); 
else if (key. keycode==W_KEY_MENU) 
{ 
ret=uPresentMenus(); 
if (ret>0) 
TryExecuteCommand(ret); 
> 
else ... 
> 
} 


A common situation in moderately complex applications is the need to display different menu bars as the 
context of the application changes. 


For example, the built in spreadsheet application on the Series 3a has a different menu bar when running 
in graph mode compared to that when running in normal mode. 


In switching between different menu bars in this way, the "position" of the menu item highlighted in one 
menu bar is often lost after switching to a different menu bar. In other words, after switching back to the 
original menu bar, the menu item highlighted is different to the one that was highlighted when this menu 
bar was last displayed. This problem is often a cause of irritation to users. 


This difficulty can be avoided by using the global variable MenuPositions available on the Series 3a 
only. The following code fragment illustrates its use: 


GLREF_D UWORD *_MenuPositions; 
LOCAL_D UWORD mi; 
LOCAL_D UWORD m2; 
ee.  /* build main menu bar */ 
_MenuPositions = &m1; 
uPresentmenus( ); 


---  /* build alternative menu bar */ 
_MenuPositions = &m2; 
uPresentmenus( ); 

ee. /* re -build main menu bar */ 
_MenuPositions = &mi; 
uPresentmenus( ); 


In essence, Hwif uses m1 and m2 to store the “position” of the menu item highlighted. Before presenting a 
particular menu bar, the address of the corresponding uworD variable (i.e. m1 or m2) should be loaded into 
_MenuPositions. 


INT uOpenDialog(TEXT *title); 


Prepares to display a dialog. The dialog has a title line given by the zero terminated string pointed to by 
title, unless title is NULL, in which case the title line is omitted. 


Returns 0 for success, or else a negative error - in which case the user will already have been notified of 
the error. 


On the Workabout, the dialog will be displayed in a small font if the global variable smal lFontDialog is 
set to a non-zero value before calling uOpenDialog. If this feature is used, it is recommended that the value 


82 


4 HWIF REFERENCE DOCUMENTATION 
SEE 


of _Smal\FontDialog should be set immediately prior to the call to udpenDialog, as in the following 
example: 


GLREF_D UWORD _SmallFontDialog; 


LOCAL_C VOID RunSmal lDialog¢VOID) 
{ 


_Smal | FontDialog=TRUE: 
uOpenD ij alog(NULL); 


> 
A call to u0penDialog automatically clears _smallFontDialog. 


Note that _smaltFontDiatog must not be set to a non-zero value for any dialog that is run on a Series 3 or 
Series 3a. Attempting to run such a dialog on either of these machines will result in the application being 
terminated with a panic 55. 


See below for additional examples of the use of uOpenDialog. 


9 


INT uRunDialog(VOID); 


Runs the current dialog, waiting until it is complete. 


Returns 0 if the user cancelled, a negative value if an error such as OOM occurred (in which case the 
user will already have been notified of the error), or else (as for Opl/W): 


= in the case of a dialog with action buttons, the return value is the (lower-case) keycode of the 
button pressed (unless that button was the Escape key, in which case the return value is zero) 


= otherwise, the index of the item highlighted when the dialog is terminated, counting the first line 
(which is the title line if that is present) as 1. 


If the dialog is completed successfully, all live variables specified by the items included in the dialog are 
written to, according to the values selected by the user. 


The Epoc static DatLocked is set TRUE for the duration of the call to uRunDialog. 
The call requires Window Server resources, and hence its success can never be guaranteed. 
See below for examples. 


Note that following a call to ukunDialog, any flashing cursor in the main display of the application may 
stop flashing. This will happen if any field in the dialog displayed a flashing cursor. 


Applications which display single- or multi-line edit boxes, or which otherwise incorporate a flashing 
cursor, will need in general to provide a layer of the following sort around calls to uRunDialog: 


LOCAL_C INT RunDialog(VOID) 
€ 
INT ret; 


ret=uRunDialog(); 
ReassertCursor(); 
return(ret); 

} 


(see also the later discussion on hEBEmphasise). 


iAddButtonList Add an action ist te wadiatog 
INT CDECL uAddButtonList(TEXT *but, INT code,...); 


Adds an action list of buttons to the current dialog, with each button being specified by a pair of passed 
parameters but, code. The text *but (ZTS) appears above the button and a representation of the keycode 
code appears inside the button. 


Buttons are added from the parameters passed until a NULL is encountered for the but of a pair. 


83 


PROGRAMMING IN HWIF 


For example, 
uAddButtonL ist¢"Cancel",W_KEY_ESCAPE,"Grey", 'g', "Invert", '7!,NULL); 


Returns 0 for success, or else a negative error - in which case the user will already have been notified of 
the error. 


Allowed values for code are any printable key (such as 'a' through 'z', or '+' or '*") as well as 
W_KEY_RETURN, W_KEY ESCAPE, W_KEY_DELETE_LEFT, W_KEY_SPACE, W_KEY_UP, W_KEY_ DOWN, W_KEY_RIGHT, 
W_KEY_LEFT, W_KEY_TAB, and W_KEY_MENU. Alphabetic values of code are always displayed in upper-case form 
but are returned (when the corresponding key is pressed) in lower-case form. 


If a keycode for a button is specified as negative, then if the user presses ESCAPE, that button will visibly 
depress and the dialog will be terminated (with return value 0 and without the contents of any live 
variables being overwritten). 


INT CDECL uAddChoiceList(TEXT *prompt ,UWORD *nsel,TEXT *choice,...); 


Adds a choice list to the current dialog, with prompt *prompt and live variable nsel, and choices given in 
text form by additional parameters until a NULL is encountered. 


For example, 
uAddChoiceList("Font",&font,"Standard","Bold","Small digits",NULL); 


The value of nset should initially be 1 to select the first choice in the list ("Standard” in the above 
example), 2 to select the second choice, and so forth. This is also the form in which the choice of the 
user is written back to nsel on successful completion of the dialog. 


Returns 0 for success, or else a negative error - in which case the user will already have been notified of 
the error. - 


If prompt is passed as NULL, the choice list is displayed centred horizontally in the dialog, without any 
prompt. 


INT uAddDialogItemCINT type, TEXT *prompt,VOID *data); 


Adds an item of specified type to the current dialog. 


Returns 0 for success, or else a negative error - in which case the user will already have been notified of 
the error. 


The item has prompt as specified, unless prompt is passed as NULL, in which case the item has no prompt, 
and is displayed centred horizontally in the dialog. 


Possible values of type, and the corresponding structs for data, are: 


H_DIALOG_TEXT for a text item, with struct H_DI_TEXT 

H_DIALOG_NUMBER for a numeric editor, with struct H_DI_NUMBER 

H_DIALOG_FLOAT for a floating point editor, with struct 1_DI_FLOAT 

H_DIALOG_TIME for a time or duration editor, with struct H_D1_TIME 
H_DIALOG_DATE for a date editor, with struct 4_DI_DATE 

H_DIALOG_EDIT for a non-scrolling text editor, with struct H_DI_EDIT 
H_DIALOG_SEDIT for a scrolling text editor, with struct H_DI_SEDIT 
H_DIALOG_XINPUT for a secret data input item, with struct H_DI_XINPUT 
H_DIALOG_FSEL for a filename editor or filename selector, with struct H_DI_FSEL. 


(It is also possible to use this routine to add a choice list or an action list to a dialog but in practice, the 
customised routines uAddChoiceList and uAddButonList given earlier are much to be preferred.) 


More details of each of the above item types are given in the following sections. 


84 


4 HWIF REFERENCE DOCUMENTATION 
eee 


typedef struct 
€ 
TEXT *str; 
UWORD type; 
} H_DI_TEXT; 


Possible bit values of type are: 


H_DTEXT_ALIGN_LEFT left align the text in its field (the default) 
H_DTEXT_ALIGN_RIGHT right align the text in its field 

H_DTEXT_ALIGN_CENTRE centre the text in its field 

H_DTEXT_BOLD display in bold 

H_DTEXT_UNDERLINE underline the item 

H_DTEXT_SELECTABLE give the item a bullet and allow it to be highlighted. 


The field str is a BCS giving the string to display. This is displayed in the right hand column of the 
dialog, unless prompt is passed as NULL in the corresponding call to uAddDiatog! tem. 


For example: 


H_DI_TEXT txt; 
TEXT buf[10]; 


if (uOpenDialog(NULL)) 
return; 
txt. type=H_DTEXT_ALIGN_CENTRE|H_DTEXT_BOLD |H_DTEXT_UNDERLINE; 
txt.str=(&buf [0] ); 
uZTStoBCS(txt.str,"Warning"); 
if (uAddDialogItem(H_DIALOG_TEXT,NULL,&txt)) 
return; 


uRunDialog(); 


typedef struct 
{ 


LONG *value; 
LONG Low; 
LONG high; 

> H_DI_NUMBER; 


The passed value of the live variable *value is what is initially displayed in the dialog. The user is 
constrained from changing the value beyond the limits tow and high. 


Setting value equal to the address of a 2-byte integer, instead of a 4-byte long integer, would be a severe 
error. 


typedef struct 
{ 
DOUBLE *value; 
DOUBLE low; 
DOUBLE high; 
> H_DI_FLOAT; 


The meanings of the fields are as for H_DI_NUMBER. 


85 


PROGRAMMING IN HWIF 


typedef struct 
{ 
ULONG *value; 
ULONG Low; 
ULONG high; 
UWORD type; 
} H_DI_TIME; 


Possible bit values for type are: 


H_DTIME_SHOW_SECONDS the time display is to include seconds (which are suppressed by 
default) 
H_DTIME_DURATION the time being edited is a duration, not an absolute time, and as 


such it never makes sense to display (eg) an am or pm alongside 
it. 


The meanings of the other fields in the H_DI_TIME struct are as for H_DI_NUMBER. Note that all times are 
expressed in seconds since midnight, regardless of the setting of the bit H_DTIME_SHOW_SECONDS. 


typedef struct 
€ 


ULONG *value; 
ULONG Low; 
ULONG high; 
> H_DI_DATE; 


The meanings of the fields are as for H_DI_NUMBER. 


The dates are all expressed in days since 1900. 


Useful date constants 
The following constants, defined in hwif.h, may prove useful: 


H_LAST_DAY the largest legal value for any of the three fields (when the p_DATE 
representation of date expires) 


H_FIRST_SYS_DAY the smallest value of day number that can be converted into the system-time 
representation of date (ie Ist January 1970) 


H_LAST_SYS_DAY the largest value of day number that can be converted into the system-time 
representation of date. 


(No value is defined for what would have been #_FIRST_DAY, since this is just zero.) 


typedef struct 
€ 


TEXT *str; 
UWORD Len; 
} H_DI_EDIT; 


The field Len gives the maximum allowed length of the string. This also determines the width set aside 
for the display of the item in the dialog. 


The live variable str gives the initial contents of the string, in BCS form. The string, once edited, is also 
written back in BCS form. 


86 


4 HWIF REFERENCE DOCUMENTATION 


typedef struct 
€ 
TEXT *str; 
UWORD len; 
UWORD width; 
} H_DI_SEDIT; 


The meaning of the fields is as for H_DI_EDIT, except that the width set aside for display purposes is given 
by width full character widths. 


typedef struct 
{ 
TEXT *str; 
> H_DI_XINPUT; 


The field str must point to a buffer long enough to hold a BCS with eight characters. 


typedef struct 
{€ 
TEXT *fname; 
UWORD flags; 
> H_DI_FSEL; 


The live variable fname must point to a buffer of at least 128 characters. This is used to seed the file 
selector and also to receive the filename chosen. 


If the bit H_FILE_NEW_EDITOR is set in flags, a filename editor is produced, with behaviour governed by the 
following remaining bits in flags: 


H_FILE_ALLOW_DIRS allow the user to choose a directory name 
H_FILE_JUST_DIRS force the user to choose a directory name 
H_FILE_FORCE_NXIST force the user to choose the name of a file that doesn't already exist 


H_FILE_NO_AUTOQUERY disable the "Confirm overwrite?" dialog that appears, by default, when the 
user types the name of a file that already exists 


H_FILE_ACCEPT_NULL allow the user to leave the field blank 


H_FILE_SET_DEFEXT set the default extension from the seed name passed (so that if the seed is 
first.pic and the user types second, the filename returned to the program is 
second. pic) 


H_FILE_CAN_ WILDCARD allow the user to type in wildcards (such as *.pic) 


If the bit H_FILE_NEW_EDITOR is nor set (there is a #define H_FILE_PICK_SELECTOR equal to zero), a filename 
selector is produced, with behaviour governed by the following remaining bits in flags: 


H_FILE_ALLOW_DIRS allow the user to choose a directory name 
H_FILE_JUST_DIRS force the user to choose a directory name 


H_FILE_RESTRICT_LIST restrict the set of files initially selectable (ie until the user presses TAB) to those 
with extension matching that of the seed filename passed 


H_FILE_ACCEPT_NULL allow the user to leave the field in a state in which no filename is selected - 
displaying, for example, (no files). 


H_FILE_SET_DEFEXT set the default extension from the seed name passed (so that if the seed is 
first.pic and the user selects second, the filename returned to the program is 
second. pic) 


H_FILE_CAN WILDCARD allow the user to specify wildcards (such as *.pic) - by means of the 
CONTROL+TAB, CONTROL+ENTER mechanism 


87 


INT uBeginDCL(H_DI_CHOICE *pch); 


Prepares to add a dynamically-defined choice list to a dialog. 


The routine writes into the passed H_p1_CHoIcE struct, which is declared and provided by the caller. The 
address of this same struct should be passed to subsequent calls to uGrowDCL and uAddDCL. 


Returns 0 for success, or else a negative error - in which case the user will already have been notified of 
the error. 


See below for an example. 


INT uGrowDCL(H_DI_CHOICE *pch, TEXT *choice); 


Adds an entry whose text is given by *pchoice to the end of the dynamically-defined choice list identified 
by *pch. 


Returns 0 for success, or else a negative error - in which case the user will already have been notified of 
the error. 


See below for an example. 


INT uAGGDCL(TEXT *prompt,UWORD *nsel,H_DI_CHOICE *pch); 


Adds a dynamically defined choice list to the current dialog, with prompt *prompt and live variable nsel, 
and choices defined by *pch. 


Returns 0 for success, or else a negative error - in which case the user will already have been notified of 
the error. 


For example, the following routine builds up a choice list whose contents are the twelve month names: 


LOCAL_C INT AddMonthChoiceList(UWORD *pmonno) 
€ 
H_DI_CHOICE ch; 
TEXT mon [32]; 
INT i; 


if (uBeginDCL(&ch)) 


return(-1); /* report failure to caller */ 
for (i=O; 1<12; i++) 

{ 

)_nmmon{ &mon [0] , 1); 

if CuGrowDCL(&ch,&mon[0] )) 

return(-1); /* report failure to caller */ 

3 

return(uAddDCL("Month", pmonno, &ch)); 


> 
No access should be made to the contents of *pch after adding it into a dialog. 


The calls uBeginDcL and uGrowDcL each allocate memory which is added into the central control block of 
the current dialog only when a subsequent call uAddoct is made. If no such call is made, the memory will 
remain permanently tied up. (However, if any call uGrowoct fails, this memory is automatically freed 
before reporting back the error.) 


VOID uAddGreyUlinme(UBYTE accel ,UWORD *plines); 


Built in applications have the ability to add grey lines underneath menu items. This function allows Hwif 
programs to do the same. 


Grey underlining serves to group related menu items and can be a useful visual aid if used sparingly. 


88 


4 HWIF REFERENCE DOCUMENTATION 
Ee re ae 


To use this function, the parameter accel must contain the accelerator character corresponding to the 
menu item under which a grey underline is to be placed. The accelerator character is the first character in 
each entry of the table pointed to by _cmds (see the section on Menu bar interactions in the Introduction to 
Awif chapter in this manual). 


The parameter pl ines must point to a memory location containing 16 words (256 bits) provided by the 
application; this area should be initialised to zero. 


Further, the global variable _GreyLines should be declared in the application and should contain the 
address of this area before the menu bar is displayed 


The function is implemented as follows: 


GLDEF_C VOID uAddGreyUline(UBYTE accel ,UWORD *plines) 
€ 
*(plines+(accel/16)) [= (1<<¢accel%16)); 
} 


It merely sets a bit corresponding to the value of the accelerator character in the 16 word area provided 
by the application. 


Some points should be noted: 


= This function cannot be used if running on the Series 3 or in compatibility mode on the Series 
3a. 


= Menu items can be safely underlined in grey even if grey is not enabled for the main console 
window. 


The following code is an example of the use this function. 


#include <p_std.h> 
#include <wlib.h> 
#include <hwif-h> 


GLREF_D UWORD _UseFull Screen; 
GLREF_D UWORD * GreyLines; 


LOCAL_D WORD keystat; 
LOCAL_D INT gc; 
LOCAL_D UWORD lines{16]; 


LOCAL_D TEXT *cmds[] = 
{ 
"nNew file", 
"oOpen file", 
"aSave as", 
"sSave", 
“iInsert", 
"cCopy", 
"delete", 
"gChange group", 
"tChange type", 
"bChange subentry", 
"pSet preferences", 
"XExit", 
NULL 
5 


GLDEF_D TEXT ** cmds = &cmds [0]; 


LOCAL_D H_MENU_DATA mdata{[] = 
€ 
"Fi le",4, 
"Edit",3, 
"Changes",3, 
"Special",2, 
NULL 
3 


89 


PROGRAMMING IN HWIF 
SSS 


GLDEF_D H_MENU_DATA *_mdata = &mdata(0]; 


LOCAL_C VOID ManageCommand(INT index) 


€ 
switch( index) 
€ 
case 0: 
break; 
case 11 : 
p_exit(0); 
default: 
break; 
> 
} 


LOCAL_C VOID SpecificInit (VOID) 
€ 
_GreyLines = &lines [0]; 
p_bfilc&lines [0] ,sizeof(lines),0); 
uAddGreyUline('o',&lines [0] ); 
uAddGreyULine('g' ,&l ines [0]; 
uAddGreyULine('t' ,&l ines [0] ); 


if (_UseFul lScreen) 
€ 
uEnableGrey(); 
gc = gCreateGCO(CuFindMainWid()); 
gBorder2(W_BORDER_TYPE_1,W_BORD_CORNER_4| 
W_BORD_SHADOW_ON |WBORD_SHADOW_D); 
> 
else 
€ 
gc = gCreateGCO(uF indMainwWid()); 
gBorder(W_BORD_CORNER_4|W_BORD_SHADOW_ON| 
W_BORD_SHADOW_D); 
} 
> 


LOCAL_C VOID TryExecuteCommand(INT keycode) 
€ 
keycode = uLocateCommand(keycode); 
if (keycode >= 0) 


ManageCommand( keycode); 
> 
LOCAL_C VOID MainLoop(VOID) 
€ 
WMSG_KEY key; 
INT ret; 
FOREVER 
€ 
uGetKeyA(&keystat, &key); 
p_iowait(); 
if (key.keycode & W_SPECIAL_KEY) 
£€ 
key. keycode &= (“W_SPECIAL_KEY); 
if (key.keycode == 'x') 
p_exit (0); 
TryExecuteCommand( key. keycode); 
> 
else if (key. keycode == W_KEY_MENU) 
€ 
ret = uPresentMenus(); 
if (ret > 0) 
TryExecuteCommand( ret); 
} 
> 
> 


90 


4 HWIF REFERENCE DOCUMENTATION 
—_—_ eee 


GLDEF_C VOID main(VOID) 
€ 
_UseFullScreen = TRUE; 
uCommoninit(); 
SpecificInit¢); 
MainLoop(); 
} 


Note the use of _GreyLines and the implementation of grey underlining inserted at the beginning of 
SpecificInit¢). The effect is as illustrated below: 


{New file =N) 
fOpen file 0) 
iSaveas =A 
A Save =} 


VOID uSetDialogUlineCINT pos, INT on); 


Built in applications have the ability to add or remove a solid underline to components in a dialog. This 
function allows Hwif programs to do the same. 


Underlining serves to group related dialog components and can be a useful visual aid if used sparingly. 


The parameter pos specifies the number of the dialog component under which a line is to be inserted or 
removed. A zero value refers to the title line while a value of one refers to the immediately following 
dialog component, and so on. The value of pos must lie in the inclusive range from zero to one less than 
the number of lines in the dialog (including the title line), otherwise the results are unpredictable. 


The parameter on specifies either a zero or a non-zero value; a non-zero value means that an underline is 
to be inserted while a zero value means that any underline is to be removed. 


The function is usually called before calling uRunDialog. 


By taking the example given in the description of uAddGreyul ine earlier and by adding the 
RunSampleDialog function and modifying the ManageCommand function as shown below, the following dialog 
display results. 


Sample Diatog 


(RRA <Itena> 
# Choice List Itemx 
j‘Numeric Editor 1 


| Text Item aaaagh 


#define ON 1 
#define OFF 0 


LOCAL_C VOID ManageCommand(INT index) 
{ 
switch¢( index) 
€ 
case 7: 
RunSampleDialog(); 
break; 
case 11 : 
p_exit(0); 
default: 
break; 
} 


91 


PROGRAMMING IN HWIF 
_—_—_— SSS 


LOCAL_C VOID RunSampleDijalog(VOID) 
€ 
UWORD chisel = 1; 
UWORD ch2sel = 1; 
LONG nEditorValue = 1L; 
H_DI_NUMBER nEditor; 
H_DI_TEXT  titem; 


nEditor.value = &nEditorValue; 


nEditor.low = 0; 
nEditor.high = 100; 
titem.str "“aaaaagh"; 


tltem.type = H_DTEXT_ALIGN_CENTRE; 


udpenDialog("Sample Dialog"); 


uAddChoiceList("Choice List 1",&chtsel,"Item a","Itemb", "Item c",NULL); 
uAddChoiceList("Choice List 2",&ch2sel ,"Item x","Itemy",NULL); 


uAddDialogI tem(H_DIALOG_NUMBER, "NumericEditor", &nEditor); 
uAddDialogItem(H_DIALOG_TEXT,"Text Item", &tItem); 


uSetDialogUL ine(2,0N); 
uSetDialogULine(3,0N); 


uRunDialog(); 
> 


The above illustration was produced on a Series 3a in non-compatibility mode. 


Underlining in dialogs 
By default, the title of a dialog is always underlined on all machines. 
There are, however, the following differences in behaviour between the different machine types: 


= on the Series 3a and the Workabout, in both compatibility and non-compatibility mode, any 
number of dialog components can be underlined. 


= on the Series 3a and the Workabout, in both compatibility and non-compatibility mode, if the 
title underline is to be removed then it must be done explicitly calling usetDialogUL ine(0,0). 


= on the Series 3, only one underline is permitted in a dialog; therefore, inserting an underline 
under a component other than the title causes the title underline itself to be removed. 


SSE a a 
The auxiliary functions in the u-layer of Hwif 


process 
VOID uEscape(UWORD flag); 


If flag is FALSE, prevents the application from being automatically terminated if the user presses 
PSION +ESCAPE. Otherwise, enables this behaviour (which is the default). 


For example, 


LOCAL_C VOID Specificinit¢VOID) 
€ 
uEscape( FALSE); 
MainWid=uF indMainWid(); 
CreateGC(); 
hCrackCommandL ine(); 
Smal lScreen(); 
OpenEditors(); 
DrawBorders(); 
wsEnable(); 
> 


92 


4 HWIF REFERENCE DOCUMENTATION 
EEE 


UINT UFindMainwWid(VOID); 


Returns the ID of the graphics window opened by the Console. This is required as a parameter to many 
WIlib calls, such as gCreateGc and wSetWindow. 


See above for an example. 


VOID uForceToFront(VOID); 


Of use mainly when an error has arisen that must be notified to the user, when the application might be 
in background (eg processing a Shutdown message from the System Screen). 


VOID uErrorString(TEXT *message); 


Presents an alert consisting of the message passed, in a manner guaranteed not to fail with OOM. 
While the alert is on screen, the application appears as “Busy” in the System Screen. 
The application is forced into foreground (if not already there). 


INT uErrorValue(INT ret); 


Presents a failsafe alert, similar to that presented by uErrorstring, except that: 
=" nothing happens if ret is zero | 
" otherwise, the message displayed is the system error text for the error number passed as ret 
® in all cases, the value ret is returned from the call itself. 

This routine is more useful than might at first be thought. 

For example, 


LOCAL_C INT DoMerge(TEXT “pb, INT flags, INT dir) 
€ 
UINT state; 


state=DbfStateDisabled; 
return(uErrorValue(DbfCopyFile(&state,dH,pb, flags, 1,dir))); 
Be 


or 


LOCAL_C VOID ReduceScreenSize(VOID) 
€ 
W_WINDATA wd; 


wd.extent.tl.x=wd.extent.tl.y=0; 

wd. extent .width=189; 

wd.extent .height=80; 

wSetWindow(MainWid,W_WIN_EXTENT, awd): 

if CuErrorValue(wCheckPoint())) 
p_exit(0); 


> 


VOID *uCheckHandle{VOID *handle); 
If handle is zero, presents an appropriate error message. Otherwise does nothing. 


In either case, returns handle. 


93 


PROGRAMMING IN HWIF 


For example (to give the source code for uBeginDCL): 


GLDEF_C INT uBeginDCL(H_DI_CHOICE “*pch) 
€ 
pch->u.count=1; 
pch->menu=uCheckHandle(hChoiceOpen()); 
return(! CINT)pch->menu); 


VOID uZTStoBCS(TEXT *bes, TEXT *zts); 


Writes the length of *zts (excluding the terminating zero) to *bes and copies *zts (again excluding the 
Zero) to *(bes+1). 


For example, to produce some text centred in the top line of a dialog, but without the usual underline: 


H_DI_TEXT txt; 
TEXT buf [20]; 


if (uOpenDialog(NULL)) 
return; 

uZTStoBCS(&buf [0] ,"Time is now"); 

txt. type=H_DTEXT_ALIGN_CENTRE; 

if (uAddDialog] tem(H_DIALOG_TEXT,NULL,&txt)) 
return; 


INT CDECL uDialogMenu(TEXT *title, TEXT *pb,...); 


Presents a dialog of text items that functions as a sort of menu, for example as the first level in a Help 
dialog suite. 


The dialog has *titte in its title line, and then subsequent choices, determined by following parameters 
pb, ..., until a NULL is encountered. The choices are each selectable. 


Following the standard rules for a dialog, returns 0 if the user cancelled or a negative value if an error 
occurred (such as OOM) - in which case the user will already have been notified of this fact - or else the 
index of the item chosen. 


For example: 


choice=uDialogMenu("Help on which topic", 
"Cursor position", 
"Select regions", 
"Drawing lines", 
"Brushes", 
"Using clipboards", 
"Borders and frames",NULL); 
switch (choice) 


VOID CDECL uDisplayText(TEXT *pb,...); 


Presents a dialog with no title, every line of which is left-aligned text - for example, as a subsidiary 
dialog in a Help dialog suite. 


Lines are added to the dialog until a NULL is encountered in the argument list. 


94 


— 


4 HWIF REFERENCE DOCUMENTATION 
__ eee 


For example: 


uDisplayText("To move the cursor:", 
“use the usual cursor keys", 
“including Home, End, PgUp and PgDn", 
“or use “Goto' in the “Edit' menu.", 
"Use Enter or Space to toggle the pixel", 
"at the cursor position.",NULL); 


SSS ee er eS 
Time-text utility functions 


The Hwif time-text functions provide a convenient way of generating textual representations of times 
and/or dates that reflect the user's preferences as given in the Formats dialog in the Time application. 


VOID *hTTOpen(VOID); 


Opens a channel for use by subsequent time-text utility functions. Returns NULL on error (OOM) - in 
which case the user will already have been notified - or otherwise a handle to be used in subsequent 
hTTxxx calls. 


See below for an example. 


It is possible to have more than one time-text channel open at any one time. Two channels might differ as 
regards the formats specified for them, or times set into them. 


VOID hTTSetAbbreviations(VOID *handle, INT dayabb, INT monabb); 


Configures the specified time-text channel so that any day names rendered by it are abbreviated to dayabb 
characters, and any month names rendered are abbreviated to monabb characters. 


To abbreviate (eg) day names but not month names, give monabb a value larger than any expected month 
name - 40, for example. 


See below for an example. 


VOID hTTSetFormat(VOID *handle, INT values); 


Configures the specified time-text channel so that any text rendered by it subsequently conforms to the 
bits present in values as follows (all the bits affect any date strings produced, except the last, which 
affects any time strings produced): 


H_TIME_FORMAT_NO_DAY Omit the number of the day in the month 
H_TIME_FORMAT_NO_MONTH Omit the number of the month in the year 
H_TIME_FORMAT_NO_YEAR Omit the year number 
H_TIME_FORMAT_MONTH_NAME Include the name of the month 
H_TIME_FORMAT_SUFFIX Include a suffix after any day number 
H_TIME_FORMAT_DAY_NAME Include the name of the day 
H_TIME_FORMAT_NO_CENTURY Include the century in any year 
H_TIME_FORMAT_NO_SECONDS Include seconds 


In all cases, the sense of the bit indicates what the default is. For example, the century is given unless the 
bit H_TIME FORMAT_NO_CENTURY is set, whereas no month name is given unless the bit 
H_TIME_FORMAT_MONTH_NAME is set. 


95 


PROGRAMMING IN HWIF 
eee 


At the same time as a call to hTTSetFormat is made, the time-text channel records the user's preferences, 
as indicated via the Formats dialog in the Time application, for the following components of any textual 
representation of date and time: 


= whether to use 12 or 24 hour clock 
= whether the day name comes before or after the month name 
= what time and date separators to use. 


A second call to hTTsetFormat completely wipes out the effect of any previous such call on the same 
channel. (However, it has no effect on the result of a previous call to hTTSetTime Or hTTSetAbbreviations.) 


See below for an example. 


INT hTTSetTime(VOID *handle, INT type, VOID *data); 


Sets the time and date to be represented in the next string produced from the specified time-text channel. 
The time and date can be given in any of four ways, depending on the value of type passed: 
H_TIME_SET_SDATE data points to a ULONG giving the time and date in system format 
H_TIME_SET_DATE data points to a P_DATE representation of time and date 
H_TIME_SET_DAYSEC data points to a P_DAYSEC representation of time and date 


H_TIME_SET_NOW the value of data is ignored and the time and date are set from the current 
system time. 


Returns zero unless an illegal value was passed (which is a programming error), causing underflow or 
overflow. i 


See below for an example. 


INT hTTSenseString(VOID *handle, INT type, TEXT *buf); 


Writes a string of the requested type from the specified time-text channel into the passed buffer. 
The types of string that can be requested are: 

H_TIME_SENSE_TIME just write the time 

H_TIME_SENSE DATE just write the date 

H_TIME_SENSE_BOTH — write both the time and the date. 
The application must ensure that buf points to a sufficiently long buffer to receive the text written. 


No terminating zero is written; instead, the length of the string produced is returned from the 
hTTSenseString call. 


For example, the following code 


VOID *tth; 
TEXT buf [48]; 


ttH=hTTOpenc); 

hTTSetFormat(ttH, H_TIME_FORMAT_DAY_NAME|H_TIME_FORMAT_MONTH_NAME); 
hTTSetAbbreviations(tthH,3,40); 

hTTSetT ime(ttH, H_TIME_SET_NOW,NULL); 

buf hTTSenseString(ttH,H_TIME_SENSE_DATE, &buf [0] )j=0; 
hTTClose(ttH); 


would result in today's date being written as a ZTS into buf{], in the form "Wed, 15 January 1992” on 
an English language Series 3 with default settings. 


Note that the above routine omits to check the value of ttu after the call to hTTOpen. This would be 
permissible for a call to hTTOpen during the initialisation of an application (provided its start-up heap was 
properly calibrated), but (possibly) not during its steady-state phase. 


96 


4 HWIF REFERENCE DOCUMENTATION 
eee 


VOID hTTClose(VOID *handle); 


Frees the resources allocated for the specified time-text channel. 


It is unnecessary to make this call if the time-text channel is used only for some of the lifetime of an 
application. Any application which uses a time-text channel throughout its lifetime has no need to close 
the channel down prior to exiting. 


See above for an example. 


SELES ee] 
Stand alone date/time text editor functions 


These are a set of functions which construct and manipulate stand-alone date/time editors. As such, they 
need not exist within a dialog. In many respects they are similar to the time-text functions but have fewer 
date/time formats. 


VOID *hDTOpen(H_OTEDIT *dte); 


Creates a stand alone date/time text editor for use by subsequent date/time editor functions. Returns NULL 
on error (OOM) - in which case the user will already have been notified. On successful completion, it 
returns a handle to be used in subsequent hDTxxx calls. 


A pointer to a struct of type H_DTEDIT must be supplied. This enables initialisation information to be 
supplied to the function. For example, the position of the date/time text editor within a window can be 
specified. 


The format of H_DTEDIT is shown below. 


It is possible to have more than one date/time text editor open at any one time. Two editors might differ 
in their formats or the date/time(s) set into them. 


VOID hDTClose(VOID *dte); 


Closes the stand alone date/time text editor and frees any resources used. The handle of the editor must 
be passed to this function. 


VOID hDTSet(VOID *dte, H_SE_DTEDIT *sdte); 


Sets a value in the date/time text editor. dte contains the handle of the text editor and the source for the 
value to be set is found in a struct of type H_SE_DTEDIT pointed to by sate. 


The format of struct H_SE_DTEDIT is as shown below. 


VOID hDTSense(VOID *dte, H_SE_DTEDIT *sdte) 


Retrieves the current information held by the date/time text editor. dte contains the handle of the text 
editor and the retrieved information is placed into a struct of type H_SE_DTEDIT pointed to by sdte. 


The format of struct H_SE_DTEDIT is as shown below. 


INT hDTSelfCheck(VOID *dte); 


The date/time text editor performs a validation of the value it currently holds and will return a non-zero 
value if the check fails. The handle of the text editor is contained in dte. 


97 


PROGRAMMING IN HWIF 


INT hDTHandleKey(VOID *dte, INT keycode, INT modifier) 


Delivers the specified keypress to the date/time text editor whose handle is contained in dte. 


The meaning of the return value has little significance for Hwif programming (date/time editors, once 
initialised, can never subsequently fail with OOM). 


VOID hDTEmphasis(VOID *dte, INT flag); 


Changes the emphasis for the date/time text editor. If flag is TRUE, the text editor is highlighted (and 
should gain the keyboard focus). If flag is FALSE, any highlighting is removed from the text editor. 


The handle of the text editor is contained in dte. 


typedef struct 
{ 
UWORD flags; 
LONG value; 
LONG low; 
LONG high; 
P_POINT pos; 
UWORD win; 
UWORD width; 


UWORD emph: 
} H_DTEDIT 


Possible values for flags are: 


H_DTEDIT_DDMMYYYY date/time text editor has Date format in the shape: 
DD/MM/YYYY; for example: 01/01/1993 
H_DTEDIT_HHMMSS date/time text editor has Time format in the shape: HH:MM:SS. 


Depending on the system settings, HH can be in 24 hour or 12 
hour format. If in 12 hour format then the time is followed by am 
Or pm as appropriate; for example: 

02:20:30 pm or 14:20:30 


H_DTEDIT_HHMM date/time text editor has Time format in the shape: HH:MM. 
Depending on the system settings, HH can be in 24 hour or 12 
hour format. If in 12 hour format then the time is followed by am 
or pm as appropriate; for example: 02:20 pm or 14:20 


H_DTEDIT_HHMMSS_D date/time text editor represents a time duration in the shape 
HH:MM:SS 

H_DTEDIT_HHMM_D date/time text editor represents a time duration in the shape 
HH:MM 

H_DTEDIT_SET_VALUE If this flag is set, then the current date/time in the editor is set to 


value, otherwise it is set to the default (the current date or time) 


H_DTEDIT_SET_LOW If this flag is set, then the minimum date/time in the editor is set 
to low, otherwise it is set to the default 


H_DTEDIT_SET_HIGH If this flag is set, then the maximum date/time in the editor is set 
to high, otherwise it is set to the default 


98 


4 HWIF REFERENCE DOCUMENTATION 


typedef struct 
{ 
UWORD flags; 
LONG value; 
LONG Low; 
LONG high; 
> H_SE_DTEDIT 


Possible values for flags are the H_DTEDIT_SET_ flags explained above. 


SSS Ee a ee eS ee ee ey 
Edit box functions 


hEBO 


VOID *hEBOpen(INT flags,H_EDIT_BOX *heb); 


Opens an edit box, as specified by flags and by *heb. 


Returns the handle of the edit box, if successful, or else NULL (in which case the user will already have 
been informed of the failure). The handle should be used to identify this edit box, as opposed to others an 
application may have, in subsequent hEBxxx calls. 


The _ED1T_BOx struct is defined as follows: 


typedef struct 
¢ 
UWORD maxchars; 
UWORD vulen; 
UWORD vislines; 
P_POINT pos; 
UWORD win; 
UWORD font; 
UWORD style; 
UWORD lead_tot; 
UWORD lead_top; 
> H_EDIT_BOX; 


It is not necessary for all the fields in this struct to be filled in before a call to hEBOpen is made. Default 
values are supplied for some of the fields. These defaults are overridden by the supplied values only if 
corresponding bits are set in flags. 


Possible bits set in flags are as follows: 


H_EDIT_BOX_CLIPBOARD The edit box is to allocate extra resources so that it can perform 
the clipboard functions Paste and Copy (with deleted text of size 
more than one character automatically being cut into the 
clipboard, as standard for Series 3 editors) 


H_EDIT_BOX_LEFT_CURSOR A triangular pointing cursor is to be displayed down the left hand 
side of the edit box, opposite the line with the flashing cursor 


H_EDIT_BOX_FONT The font and font style for the edit box are to be as specified in 
the fields font and style in “heb (the defaults are the system font 
with normal style); possible values might be w_FoNT_BASE+1 for the 
bold font, and 6_sTY_ITALIc for an italic style 


H_EDIT_BOX_LEADING The leading for the edit box font is to be as specified in the fields 
lead_tot and lead_top in *heb (the defaults being 2 and 1 
respectively): the former being the total extra vertical spacing 
between lines, in addition to the font height, and the latter being 
how much of the total leading applies at the top of each line. 


H_EDIT_BOX_VISLINES The edit box is to be heb->vislines lines high (the default is one 
line) - although in all cases, it will scroll vertically if enough text 
is added to form more lines than can be seen at once. 


PROGRAMMING IN HWIF 
ee SSSeeeSeSSSSSSSSFSFSFSSSSSSSSSSFhheseFFFFFeFeeee 


The meanings of the remaining fields in the H_EDIT_Box struct, which always have to be filled in by the 
caller, are as follows: 


maxchars The maximum number of characters in total that the edit box can contain 
(before beeping at the user and displaying a Maximum number of characters 
reached information message); paragraph ends count as one character each 


vulen The total visible width of the edit box, in pixels, including any margin 
required for a left cursor; this width also implicitly defines the wrapping 
margin for multi-line edit boxes 


win The ID of the enclosing window - which is usually the value Mainwid returned 
by a call to uFindMainwid 


pos The offset of the top left of the edit box, relative to the enclosing window. 


The edit box may be further customised, before any keys are passed on to it, by means of many of the 
calls discussed below. 


INT hEBHandleKey(VOID *ebH, INT keycode, INT modifier); 


Delivers the specified keypress to the edit box. This should be either a printable character or an editing 
key. In practice, ENTER keys should only be allowed through to multi-line editors. 


Returns zero for success, or a negative value for an error (in which case the user will already have been 
notified). 


Formatting in background 


Keys which cause a change in the location of line breaks due to word-wrap are treated slightly differently 
in Hwif editors than in editors used by the built-in applications: 


# in the built-in applications, only the line containing the cursor is reformatted and redrawn at 
once, before the edit box checks to see if another keypress is ready to be processed; lines further 
from the cursor are reformatted and redrawn, if needed, as a background activity in pauses 
between the receipt of incoming keys 


= in Hwif editors, any subsequent key is processed only when the edit box has been completely 
reformatted and redrawn. 


The difference in performance only becomes apparent for larger editors with longer paragraphs. For 
programmers wishing to increase the responsiveness of their editors to incoming keys, the following lines 
of code may be tried: 


GLREF_D UWORD _ebControl Format; 


_ebControl Format=TRUE; 


In this case, Hwif editors will, for any one call to hEBHandleKey, only reformat and redraw one line 
(except on receipt of a cursor key). This means it is the responsibility of the programmer to make a later 
call to hEBCompleteFormat (discussed below) at a suitable moment - for example, when a call to 
uKeyPressOutstanding next returns FALSE. 


hE 


 Somplete edi 
INT hEBCompleteFormat(VOID *ebH); 


Ensures that the specified edit box is completely formatted and completely drawn. Does nothing if the 
formatting is already up to date. 


Only needs to be called explicitly by an application if the static variable _ebcontrolFormat has been set 
TRUE (see above). 


Returns zero for success, or a negative value if there was insufficient memory to complete formatting. 


100 


4 HWIF REFERENCE DOCUMENTATION 


TEXT *hEBSenseText(VOID *ebH); 

Returns a pointer to the buffer where the edit box is currently keeping its own copy of its text. 
This copy is always zero terminated, so its length can be obtained by a call to p_sten. 

The buffer may contain embedded \n's, representing paragraph ends. 


Note that the location of the buffer may change as more text is added into the edit box, so there is no 
point in an application trying to keep a permanent copy of this address. 


INT hEBSetText(VOID *ebH, TEXT *pb, INT blen); 


Sets the blen characters at *pb as the text for the specified edit box. 


Returns zero for success, or a negative value if an error occurred (in which case the user will already 
have been notified). 


VOID hEBEmphasise(VOID *ebH, INT flag); 


Either emphasises or de-emphasises the specified edit box, depending on the value of flag (TRUE to 
emphasise it, FALSE to de-emphasise it). 


An emphasised edit box displays: 
® a flashing text cursor (unless the width of the text cursor has been set to ZeTO) 
= a triangular cursor in the left margin (if the flag #_EDIT_BOX_LEFT_CURSOR was set on initialisation) 
= a highlight between the cursor and anchor points of any select region. 

A de-emphasised edit box displays none of these features. 

By default, an edit box is de-emphasised. 


Applications may wish to surround edit boxes with curved borders, with varying degrees of shadowing to 
help indicate whether or not each edit box is currently emphasised. In this case, the application has the 
responsibility for drawing the borders (using the Wlib call gBorderkRect). 


When edit boxes lose their cursor 


Note that edit boxes maintain their own record of whether they are emphasised, and do nothing if they 


receive an hEBEmphasise Call instructing them to change their emphasis state to what it already is. Thus in 
the following sequence of code 


hEBEmphasise(ebH, TRUE); 
hEBEmphasi se(ebH, TRUE); 


the later hEBEmphasise call is ignored, unless a call hEBEmphasise(ebH, FALSE) has been made in the 
meantime. 


The significance of this is as follows: suppose that, in the meantime, a flashing text cursor is drawn 
elsewhere on the screen by the application. This may be as a result of an explicit call to worawTextCursor; 
more likely, it will be as a side effect of a call to uRunDialog or hPrintSetupDialog. In this case, the text 
cursor will be removed from the edit box - since there can only be one text cursor per application at any 
one time. When the intervening text cursor is cancelled - say as the result of the termination of the dialog 
- it is the responsibility of the application to re-activate the flashing cursor where it used to be (if that is 
still appropriate). However, in the light of what has just been explained, a simple call to hEBEmphasise 
will be insufficient to effect this. 


Accordingly, applications which contain an edit box as part of their main display, and which invoke 
dialogs with items which can also display a flashing cursor, need to use a layer of the following sort 
around calls to uRunDialog: 


101 


PROGRAMMING IN HWIF 


LOCAL_C INT RunDialog(VOID) 
£ 
INT ret; 


hEBEmphasise( edit femph] , FALSE); 
ret=uRunmDialog(); 
hEBEmphasise(edit [emph] , TRUE); 
return(ret); 

3 


A similar protective layer is needed around any calls to hPrintSetupDialog. 


INT hEBSetSelect(VOID *ebH, INT aoff,INT coff); 


Sets the anchor point and cursor point of the specified edit box. Both positions are given as character 
offsets into the content of the edit box, starting at 0 for the position in front of the first character. 


Note that neither the anchor point nor the cursor point is permitted to come after the last character in the 
edit box. 


To set the cursor position without setting any highlighted select region, pass the value of aoff to be equal 
to that of coff. 


Returns zero for success or a negative error value (in which case the user will already have been 
notified). 


INT hEBSenseSelect(VOID *ebH,UWORD *top); 


Returns the length of the select region of the specified edit box, and writes the character offset of the top 
of the select region to *top. 


If there is no select region, the value 0 is returned, and the character offset of the cursor point is written 
to’ *top. 


TEXT *hEBSenseClipText(VOID *ebH); 


Returns a pointer to the buffer where the clipboard of the edit box is currently keeping its own copy of 
its text. 


The form of this buffer is exactly the same as the buffer used for the main text of the edit box (see the 
discussion on hEBSenseText above). 


INT hEBSetClipText(VOID *ebH, TEXT “pb, INT blen); 


Sets the blen characters at *pb as the text for the clipboard of the specified edit box. 


Returns zero for success, or a negative value if an error occurred (in which case the user will already 
have been notified). 


INT hEBChangeWidth(VOID *ebH, INT width); 


Changes the width of the specified edit box to width. The new width is specified in pixels and includes an 
allowance for any left margin required to display a left triangular cursor - exactly as for the vulen field of 
the 4_EDIT_BOx struct used to initialise the edit box. 


In the case of a multi-line editor, the text is automatically re-formatted, wrapping to the new width. In all 
cases, the display is scrolled (vertically and/or horizontally) to expose the cursor position. 


Returns zero for success, or a negative value if an error occurred (in which case the user will already 
have been notified). 


102 


4 HWIF REFERENCE DOCUMENTATION 
See 


Even if the return value is negative (indicating a failure to re-format the edit box to its new width), the 
record within the edit box of its width will be updated as requested. 


VOID hEBSetCWidth(VOID *ebH, INT cwidth); 
Changes the width of any flashing cursor displayed. The value of cwidth is in pixels. 
By default, the flashing cursor is two pixels wide. 


Passing cwidth as zero makes the flashing cursor invisible. This may be appropriate when, for example, 
highlighting some found text by means of the call heBsetSelect. 


INT hEBInsert(VOID *ebH, TEXT *pb, INT blen); 


Inserts the blen characters at *pb into the edit box at the cursor position. Any select region is cancelled 
first. The cursor is positioned afterwards to the end of the inserted text. 


Returns zero for success, or a negative value if an error occurred (in which case the user will already 
have been notified). 


Deletes any highlighted selection, into the clipboard if the edit box has one, and then inserts the contents 
of the ZTS *replace at the cursor position. The cursor is finally positioned at the end of the text inserted, 
unless backwards is TRUE, in which case it is placed at the beginning of the selected text. 


Returns zero for success, or a negative value if an error occurred (in which case the user will already 
have been notified). 


The routine was originally designed with the functionality of a Replace command in mind. See also the 
section below on hEBFind. 


INT hEBEvaluate(VOID *ebH); 
Attempts to perform the standard Evaluate function on the specified edit box. 


Retums zero for success, or a negative value if an error occurred (in which case the user will already 
have been notified). 


Typical contents of the Evaluate routine called from the ManageCommand routine of an application would 
simply be as follows: 


if (!CheckEditing()) /* check focus is positioned suitably */ 
hEBEvaluate(ebH); /* ignore any error */ 


INT hEBCopy(VOID *ebH); 


Attempts to perform the standard Copy function on the specified edit box. 


Retums zero for success, or a negative value if an error occurred (in which case the user will already 
have been notified). 


But if there is no select region (and hence nothing to copy into the clipboard), the special value 1 is 
returned. 


Typical contents of the Copy Text routine called from the ManageCommand routine of an application would 
accordingly be as follows: 


103 


PROGRAMMING IN HWIF 


if (!CheckEditing()) 
{ 
if (hEBCopy(ebH)>0) 
wWInfoMsg("No text to copy"); 
else 
wInfoMsg("Text copied"); 
} 


INT hEBPaste(VOID *ebH); 


Attempts to perform the standard Paste function on the specified edit box. 


Returns zero for success, or a negative value if an error occurred (in which case the user will already 
have been notified). 


But if the clipboard is empty (and hence there is nothing to paste), the special value 1 is returned. 


Typical contents of the Paste routine called from the ManageCommand routine of an application would 
accordingly be as follows: 


if (!CheckEditing()) 
{ 
if (hEBPaste(ebH)>0) 
wInfoMsg("No text to insert"); 


INT hEBFind(VOID *ebH, TEXT *str,INT flags); 


Attempts to find a copy of the ZTS *str in the contents of the specified edit box. If successful, returns 
TRUE and automatically creates a highlighted selection over the copy found. Otherwise returns FALSE 
(except if an error occurred, in which case the return value is negative, and the user will already have 
been notified). 


The following bits in flags determine how the search is done: 


HEB_FIND_BACKWARDS Search backwards from the cursor position (the default is to search forwards 
from the cursor position) 


HEB_FIND_CASESENS _ The search is case sensitive (the default is for a case insensitive search). 


VOID hEBClearChanged(VOID *ebH); 


Clears the internal "changed" flag of the specified edit box. 
This flag is clear when the edit box is first initialised. 


This flag is set whenever the contents of the edit box change as a result of any of the calls hEBHandlekey, 
hEBInsert, hEBEvaluate, hEBPaste, Or hEBReplace. 


Typically, an application might call hEBClearchanged following calls to hEBSetText or hEBSenseText. 


INT hEBSenseChanged(VOID *ebH); 
Returns the "changed" flag of the specified edit box. See above for some further discussion. 


INT hEBShowSymbols(VOID *ebH, INT flag); 


Shows or hides end of paragraph symbols and visible space markers, depending on the value of flag 
(TRUE to show them, FALSE to hide them). 


104 


4 HWIF REFERENCE DOCUMENTATION 
———— eee 


These symbols are hidden by default. 


Returns zero for success, or a negative value if an error occurred (in which case the user will already 
have been notified). 


Even if the return value is negative (indicating a failure to re-format the edit box), the record within the 
edit box of whether to show these symbols will be updated as requested. 


VOID hEBClose(VOID *ebH); 


Frees the resources allocated for the specified edit box. 


Any application which uses an edit box throughout its lifetime has no need to close the edit box prior to 
exiting as any resources used will automatically be freed when the application terminates. However, it 
may be sensible to close an edit box explicitly if it is only used for a short period during the lifetime of 
the application. This avoids resources being tied up unnecessarily. 


VOID *hEBSenseDoc(VOID *ebH); 


This function returns the handle of the document component of an edit box allowing it to be manipulated 
by other Hwif functions. 


In some circumstances, the manipulation of the document component rather than the whole edit box can 
significantly reduce overhead (see hEDCapacity, hEDInsert, hEBDocChanged). 


The parameter ebh must contain the handle of the opened edit box whose document component is 
required. 


INT hEDCapacity(VOID *doc,UINT maxlen); 


This function sets the capacity of the document component of an edit box. In other words, it specifies the 
maximum size of the text that the edit box can hold. 


The parameter doc must contain the handle of the document component of the edit box as returned from a 
call to hEBSenseDoc. The maximum size of the text is specified in parameter maxlen. 


The function returns zero if successful or a negative value otherwise. If an error occurs, the user will 
already have been notified. 


INT hEDInsert(VOID *doc,UINT pos, VOID *buf,UINT len); 


This function inserts text into the document component of an edit box. 


The parameter doc must contain the handle of the document component of the edit box as returned from a 
call to hEBSenseDoc. 


The text to be inserted is located at buf and is ten bytes long. The text is inserted at offset pos within the 
document component. 


The function returns zero if successful or a negative value otherwise. If an error occurs, the user will 
already have been notified. 


hEB 


INT hEBDocChanged(VOID *ebh); 


Notifies the edit box that the content of its document component has changed. This causes the text to be 
re-formatted. 


The parameter ebh must contain the handle of the edit box to be notified. 


The function returns zero if successful or a negative value otherwise. If an error occurs, the user will 
already have been notified. 


105 


PROGRAMMING IN HWIF 


Note that no attempt is made to validate the cursor position. It is the programmer's responsibility to set 
this correctly. 


INT hEBSetMargin(VOID *ebh,UINT right); 


This function allows the right hand word-wrap margin of an edit box to be set. This value, in effect, sets 
a limit on the amount of text that can be displayed on one line before being wrapped around onto the next 
dine. 


The parameter ebh must contain the handle of the edit box whose margin is to be set. The margin itself is 
defined by the value in the parameter right; this value is measured in pixels. 


Setting right to a large value such as 4096, effectively turns off word-wrap. In fact this is the only 
sensible use of this function. If there were a need to set a margin smaller than the width of the edit box 
then it would be easier to use a smaller edit box. 


The following code fragment suggests how it might be used to turn off word-wrap: 


H_EDIT_BOX heb; 
VOID * ebh; 


ebh = hEBOpen(H_EDIT_BOX_VISLINES|H_EDIT_BOX_LEFT_CURSOR, &heb); 
hEBSetMargin(ebh, 4096); 


The function returns zero if successful or a negative value otherwise. If an error occurs, the user will 
already have been notified. 


This function senses and returns the current value of the right hand word-wrap margin of an edit box 
whose handle is passed in ebh. 


UINT hEBPosToXL(VOID *ebh,H_SCRLAY_PLX *pLx); 


This function converts a character position, within the text of the edit box whose handle is passed in ebh, 
to a position on the screen. 


The character position is passed in the pos member of the H_SCRLAY_PLX struct pointed to by plx. This 
struct is defined in Awif.h as: 


typedef struct 


> H_SCRLAY_PLX; 


If the character position is visible on the screen, the line number and horizontal pixel offset within the 
line are written to plx->line and plx->x respectively and the function returns zero. 


If the character position is above the screen, a value of -30000 is written to plx->line and the function 
returns -1. 


If the character position is below the screen or beyond the end of the text, a value of +30000 is written 
to plx->line and the function returns +1. 


106 


4 HWIF REFERENCE DOCUMENTATION 


in ee ee i er aes ee ee 
Printing and print support functions 


VOID hPrintSetupDialog(VOID); 
This is NOT to be confused with the function hPrinterSetupDialog described later. 
It presents the standard Print Setup dialog, waiting until it is complete. 


The Epoc static DatLocked is set to TRUE for the duration of the call to hPrintsetupDialog. 


As for the Print Setup dialogs in the built-in applications, the dialog sometimes allows the user to make 
choices that the printer eventually chosen cannot deliver. For example, some style combinations (Bold 
and italic) may not be supported in some fonts (in such a case, the text will probably come out either as 
bold or as italic). Again, not all printers support landscape orientation. 


VOID hPrinterSetupDialog(VOID); 


This is NOT to be confused with the function hPrintSetupDialog described earlier. 


It presents the standard Printer Configuration Setup dialog, waiting until it is complete. 
The Epoc static DatLocked is set to TRUE for the duration of the call to hPrinterSetupDialog. 


The Printer Configuration Setup dialog behaves in exactly the same way as for the built-in applications in 
that it requests printer device information and provides an entry into the print preview settings dialog. 


VOID hPrint(INT PrintLineCH_PRINT *)); 


Prints from the application, according to parameters specified by the user in any Print Setup dialog. 


The callback function PrintLine is repeatedly called by system code, as printing progresses, until printing 
terminates. Each time the function is called, the application has to return either TRUE or FALSE: 


= areturn value of TRUE indicates that more data is being passed to print; this data is written into 
the fields of the H_PRINT struct passed 


= areturn value of FALSE indicates that the application has no more data to print. 
The _PRINT struct is defined as follows: 


typedef struct 


WORD flags; 
WORD typf; 
WORD fheight; 
WORD style; 
WORD down; 
WORD indent; 
WORD height; 
WORD right; 
TEXT *buf; 
UWORD blen; 
} H_PRINT; 


Most of the fields of this struct will have already been set to suitable values before printLine is called. 
All but the most ambitious of Hwif application writers should be content to write to, at most, the 
following fields: 


blen This must be set if the bit H_PRINT_TEXT is set in flags (as it is by default). It 
gives the number of characters to be printed. 


107 


PROGRAMMING IN HWIF 
— SSS 


buf This must be set if blen is non-zero (and H_PRINT_TEXT is set in flags). It gives 
the address of a contiguous buffer holding the blen characters to be printed. 
Note that the buffer must be permanent (as opposed to being declared on the 
stack of the routine PrintLine). 


indent This (zero by default) specifies the additional amount to indent the print 
position by, horizontally, prior to the specified text being printed. Any value 
given must be in printer units (see below). 


flags The bit H_PRINT_KEEP may be ORed in, to request that, if possible, this line of 
text be kept together on the same page with the following line; the bit 
H_PRINT_PAGE may be ORed in to force the emission of a form feed prior to the 
line being printed. 


style Any of the bits H_PRINT_STY_UNDERLINE, H_PRINT_STY_BOLD, H_PRINT_STY_ITALIC, 
H_PRINT_STY_SUPER, and H_PRINT_STY_SUB may be ORed in, to further embellish 
the font chosen by the user, in the Print Setup dialog, as the default font. 


Note that in both the last two cases, it is crucial to or in the bits required, rather than simply setting them 
with an assignment statement. Note also that in the case of styte, there is no guarantee that just because a 
particular font style is set, the current printer will be able to fulfil the request made on it. 


Word wrapping during printing 


Before any text is printed, ROM resident printing code checks that it will fit in the width available to it. 
If not, word wrap is performed, with any excess being printed on the subsequent line instead. This 
subsequent line is printed without any additional call to the printLine function. That is, more than one 
line may be printed as a consequence of any one call to PrintLine. 


The word-wrap calculation performed makes the following assumptions: 


= the text is to fit into the full width of the page, minus its left and right margins, and minus any 
indent specified j 


= — the text is to be printed in the font and style specified by the user in the Print Setup dialog. 


Some fonts may change their width if (for example) they are bolded or italicised; if the application ors 
any such bit into style, there is, accordingly, a risk that the word-wrap calculation will be incorrect. 
(Similarly - but even more so - if the application writes into the right field of the H_PRINT struct, or if the 
H_PRINT_LINE bit is removed from flags. See below for a discussion of these possibilities.) 


If word-wrap is required, the H_PRINT_KEEP bit is set into flags provided the user has not set the Allow 
widows/orphans choice list in the Print Setup dialog suite to "yes". 
Page breaks during printing 


Page breaks are automatically calculated by ROM resident layout software, and do not have to be 
inserted by application code. 


However, as noted above, it is possible for the application to force a given piece of text to appear at the 
top of a new page. 

Limitations during the PrintLine callback 

The application is actually in a somewhat vulnerable state during a printLine callback: 


# Only a limited amount of the Hwif system services are available to it in this state; in particular, 
no call can be made to uRunDialog, uPresentMenus, hPrintSetupDialog, hPrinterSetupDialog or 
(recursively) hPrint - on pain of indeterminate damage resulting 


= Only a limited amount of time should be spent inside each call to printLine; if an application 
remains in PrintLine indefinitely, and the application is tasked into background and then into 
foreground again in the meantime, the Printing dialog in the foreground will not be redrawn 
properly, and its Escape action button will not be effective. 


The Epoc static DatLocked is set TRUE for the duration of the call to hprint. 


108 


4 HWIF REFERENCE DOCUMENTATION 
eee 


VOID hPrintSetSI(UINT subsqind); 


Sets the indent to be used for the second and following lines of any wrapped block of text. The value 
given must be in printer units (see below). 


The subsequent indent defaults to zero. But once it is set, by one PrintLine call, it retains its value during 
subsequent PrintLine calls (unless it is changed again). 


INT hPrintSensePageWidth(VOID); 


Returns the width of the page, minus its left and right margins, in current printer units. 


INT hPrintSenseBufWidth(TEXT *buf, INT blen); 


Returns the width of the passed buffer of text, in current printer units. The width is worked out in the 
default font and style specified by the user in the Print Setup dialog. 


The result may be of use in calculating indents for lines. For example, it allows the printing of centred 
text. 


Advanced possibilities when printing 
By default, the 1_PRINT_LINE bit is always set in flags. This has the following effect: 


= before printing any text, the print position is moved down by an amount equal to the sum of 
height (equal by default to the height of the default font) and down (zero by default), except that 
down is ignored for the first line on a page 


= — the print position is moved back to the left margin, and then in by indent. 


None of these things happen if the #_PRINT_LINE bit is missing. Printing just continues from where it left 
off the previous time. 


In order to print in columns, it is possible to proceed as follows: 
= print the first column as per normal 


= the next time PrintLine is called, clear the H_PRINT_LINE and 4_PRINT_TEXT flags that are set by 
default; instead, set the H_PRINT_RIGHT flag and supply a suitable value of right 


= following that, keep the H_PRINT_LINE bit clear, but pass the text corresponding to the second 
column 


= repeat for any additional columns. 


The value specified for right has, again, to be in printer units. It can be calculated from the result of a 
call to hPrintSenseBufWidth. 


Inevitably, there are limitations with this approach. For greater control over printing, it is necessary to 
interact more directly with the object classes in the Series 3 ROM. 


ea ee ee ee ee ee ee 
Miscellaneous functions 


INT hCrackCommandL ine(VOID); 


Reads the command line and sets up appropriate initial values of various reserved statics, including 
DatUsedPathNamePtr (the full path name of any file to open or create). 


The return value has significance for file-based applications: 
‘O' the specified file already exists, and is to be opened 


109 


PROGRAMMING IN HWIF 


4or a file of the specified name is to be created and opened anew, with any existing file of that 
name to be overwritten 


0 the command line is not present (for example, the application may have been run from a 
source other than the System Screen). 


For example: 


LOCAL_C VOID SpecificInit(VOID) 
€ 
INT command; 
INT bid; 
VOID *fcb; 


command=hCrackCommandL ine(); 
bid=ObeySystemCommand( command, DatUsedPathNamePtr ,&fcb); 


VOID hSetUpStatusNames(TEXT *pb); 


Changes the value of DatUsedPathNamePtr to pb and makes other required associated changes in reserved 
Statics. 


For example: 


LOCAL_C VOID ChangeName(TEXT *newname) 
€ 
p_scpy(&fi Lename[0] ,newname); 
hSetUpStatusNames (&f i lename [0] ); 
wsUpdate(WS_UPDATE_NAME); 
> 


Note that *pb must be a fully parsed filename (such as is returned by a file selector item in a dialog). 
Further, the buffer *pb must be a permanent one (as opposed to being defined on the stack of a routine 
such as ChangeNames). 


VOID hEnsurePath(TEXT *fname); 


Ensures that the path of the specified filename exists. 
Should generally be called following a New file or Save as menu command. 
For example: 


if (RunDialog()) 


€ /* Save As dialog successfully completed */ 
savebuf [1+savebuf [0]]=0; /* BCS to ZTS conversion */ 
hEnsurePath(&savebuf [1] }; 

DoSave(&savebuf [1] ); 

winfoMsg("Saved"); 

3 


VOID hDlgPositionCINT x,INT y); 
Affects the position in which the current dialog will appear. 


If x is negative, the dialog will appear on the left edge of the screen; if positive, on the right edge; if 
zero, centred horizontally. 


If y is negative, the dialog will appear on the top edge of the screen; if positive, on the bottom edge; if 
zero, centred vertically. 


Thus in all there are 9 possible locations for the dialog. 


110 


4 HWIF REFERENCE DOCUMENTATION 
__ SSSSSSSSSSSSSSSSSSSSSFsFeFeFeFeFeseseFeFesesesSse 


In the absence of this call being made for a dialog, it is positioned in the centre of the screen, just as if 
the call 


hDigPosition(0,0); 
had been made. 


ring | 


INT hDTMFString(TEXT *zts); 


Emits DTMF tones for the passed string. 


Uses the tone lengths and pauses as specified by the user in the World application (or otherwise), 
reverting to system defaults in the absence of any such setting. 


Returns zero for success or a negative error if the sound system was unavailable (on account of being 
hogged by another application). The call embodies a double retry before failing. 


The call waits for the tones to be emitted before returning. 
For example: 


if ChDTMFString("123")) 
wInfoMsg("Sound system busy"); 


INT hIsDbfCompressible(VOID *dH); 


Returns TRUE if the database file open on database channel di is compressible (ie if it is on any medium 
other than Flash), and FALSE otherwise. 


For example, 


LOCAL_C VOID CompressFile(VOID) 
{ 
UINT state; 


if ¢(!hIsDbfCompressible(dH)) 
wInfoMsg("Cannot compress on Flash"): 
else 
€ 
state=DbfStateDisabled; 
Check(DbfCompress(&state,dH)); 
winfoMsg("File compressed"); 


VOID hHelpSubSystemC(INT startid, INT indexid); 


This function allows the developer to access the same Help engine as used by the inbuilt applications on 
the Series 3. 


Like uPresentMenus and uRunDialog, hHelpSubSystem only returns when the entire operation (in this case, 
the Help operation) has been completed by the user. It does its own error handling internally, 
automatically presenting suitable error messages where necessary. 


It is worth noting that other events cannot be notified when inside this call; messages from the system 
screen are ignored and the expiry of timers is effectively delayed until the call completes. 


The two parameters are the IDs of suitable resources in an application resource file in which the 
hierarchy of Help text is defined: 


= The first is the resource ID of the "top-level" Help resource in the resource file. 
= The second is the resource ID of the "index" set of Help resources. 


For further general information on resource files, see the Additional System Information manual. 


111 


PROGRAMMING IN HWIF 


The following "structures" are used in building Help resources. For further information and examples on 
the use of these structures in building a hierarchy of Help text, see the Introduction To HWIF chapter in 
this manual. 


STRUCT HELP_ARRAY 
{ 
LINK topic_id; 
TEXT topic; 
LEN BYTE STRUCT strist{[]; 
> 


where any instances of the strist fields must always be as STRING 


STRUCT STRING 
€ 
TEXT str; 
> 


STRUCT TOPIC_ARRAY 
{ 
LEN BYTE LINK id lst] 
} 


VOID hDeclareAppRcb(VOID *rcb); 


Before an application resource file can be used (e.g. to invoke Help), a channel to the file must be opened 
and its handle passed to the system. 


This function performs the action of passing the handle of an (already) opened application resource file 
channel to the system via the rcb parameter. 


It is called from the function hInitAppReb, described later, which opens an application's built in resource 
file. 


VOID *hInitAppReb(VOID); 


This function opens a channel to an application's built in resource file and returns its handle. The handle 
itself is passed to the system using the function hDeclareAppkeb, described earlier. 


In general, this function allows the application's built in resource file to be accessed; the handle itself is 
used explicitly by such functions as hRequestReplacePack described later. 


In actual fact the function creates and initialises a resource file object. However, for those unfamiliar 
with Object Oriented Programming, it is easier to think in terms of opening a channel. 


The function is implemented as shown below. Note again that it uses hDeclareAppgcb described earlier. 


GLDEF_C VOID *hInitAppRcb(VOID) 


€ /* create and initialise resource file object */ 
INT ret; 
VOID *rcb; /* resource file object handle wf 


rcb = p_new(1,C_RSCFILE); 
ret = p_entersend3(rcb,O_RS_INIT,DatCommandPtr); /* pass file name */ 
if (ret < 0) /* no resource file found */ 

p_exit(ret); /* fatal (programmer) error*/ 
hDeclareAppRcb(rcb); 
> 


112 


4 HWIF REFERENCE DOCUMENTATION 
SS  SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSeSe 


The implementation assumes that the application resource file is built into the .app file following 
standard conventions. 


If a resource file is to be used which is separate from the application, then the above code could be used 
but the reference to DatCommandPtr would need to be changed. In general the third parameter to the 
p_entersend3 function in the above code should contain a pointer to the resource file name. 


VOID hRequestReplacePack(VOID *rcb, TEXT *fname) 


This function is normally used when a channel to an application resource file (with handle reb) has 
previously been opened but is now found to be unavailable. This is most commonly caused by the 
removal of the pack on which the resource file is located. 


The function issues a warning message to the user which PERSISTS until the appropriate resource file, as 
specified by fname, has been loaded and found. 


fname Must point to a buffer of at least p_FNAMESIZE characters. 


The following code fragment illustrates the use of this function. It makes use of the rscfile class; this is 
documented in the resource files chapter in the additional system information manual. 


Also note that the function InitReb is, in essence, the same code that implements the utility function 
hInitAppReb, described earlier. 


It simply displays an information message where the text is taken from the resource file whose name is 
contained in fname. 


GLREF_D VOID *rcb: 
GLREF_D TEXT fname [P_FNAMESIZE] ; 


LOCAL_C VOID InitRcb(VOID) 
{ 
INT ret; 


reb = p_new(1,C_RSCFILE); 
ret = p_entersend3(rcb,O RS_INIT,&fname([0]); 
if (ret < 0) 
p_exit(ret); 
hDeclareAppRcb(rcb); 
3 


LOCAL_C VOID DoInfoMessage(INT index) 
€ 
TEXT buf [60]; 


while(p_send4(rcb,O_RS READ _BUF,&buf [0], index) < 0) 
hRequestRepl acePack(reb, &fname [0] ); a 

winfoMsg(&buf [0] ); 

> 


InitReb¢); 


index = INFO_USEFUL_MESSAGE; 
DoInfoMessage( index): 


hSetS 


VOID hSetSystemResourceLang(UINT Langnum); 


Changes the language used by system resources to the that specified by Langnum. Language numbers are as 
described for the PLIB function p_getlanguage. 


This function is useful only for programs running on multi-lingual machines. It changes the system 
resource for your single application and NOT for other applications running at the same time. 


Note that hsetSystemResourceLang does nor alter messages from the .cfo config file. 


113 


PROGRAMMING IN HWIF 


An example of its use might be: 


LOCAL_C VOID SpecificInit(VOID) 
{ 
uEscape( FALSE); 
hSetSystemResourceLang(6); 
MainWid = uFindMainWid(); 


> 
to try to set the system resource file for language number 6 (Swedish). 


VOID hSetVarrayInChlistCINT index, INT nsel, VOID *varray); 


This function permits a choice list to be added to a dialog item with index number index. The list of 
items is found in the variable array object with handle varray. The default selected item is set to array 
item nsel. 


The assumption is made that the dialog item is already a choice list. 


This function provides a means of setting a choice list containing more than 255 items. The 
implementation is as shown below. 


GLDEF_C VOID hSetVarrayInChlist(INT Index, INT nsel, VOID *varray) 
€ 
SE_CHLIST set; 


set.nsel = nsel; 

set.data = varray; 

set.set_flags = SE_CHLIST_DATA | SE_CHLIST_NSEL | SE_CHLIST_RETAIN; 
p_send4(DatDialogPtr,O_WN_SET, index, &set); 

> 


An example of the use of hSetVarrayinchlist is as shown below. Note that createVariableArray() is a 
smal] user written function which would create the variable array. 


LOCAL_C VOID LongChoiceList(VOID) 
{ 
UWORD junk; 
UWORD used; 
UWORD Longnsel; 
PR_ROOT *vastr; 


junk = 4; 
used = 0; 
longnsel = 1; 
vastr = CreateVariableArray(); 


if (uOpenDialog("Example long list")) 
return; 
if CuAddChoiceList("Ignored",&junk,"Date","Time"™,NULL) 
if (uAddChoiceList("Long choice", &used, NULL) 
return; 
hSetVarrayInChlist(2, longnsel ,vastr); 
if (uRunDialog() > 0) 


INT hLoadOwnDyl (INT index); 


This is a utility function that loads a DYL from the multiple DYL file built into the application's .app 
file and makes it ready for use. 


114 


4 HWIF REFERENCE DOCUMENTATION 
SEES 


If successful, the function returns the handle of the loaded DYL. It returns 0 if the file can not be 
opened. 


The parameter index specifies which DYL is to be loaded from the multiple DYL file. Zero refers to the 
first DYL, one refers to the second and so on. 


The function is implemented as shown below: 


GLDEF_C INT hLoadOwnDyl (INT index) 
€ 
INT DylHandle; 
VOID *fcb; 
INT ret; 


DylHandle = 0; 

ret = p_openlib(&fcb, DatCommandPtr); 

if (tret) 
€ 
p_loadfilelib¢fcb, index, byl Handle, TRUE); 
p_close(feb); 
> 

return(DylHandle); 

> 


UINT hLastSystemKey(UINT *pmodi fiers); 


This function allows the application to find out what keypress was last processed by the system on its 
behalf. 


The function returns the keypress value while the bit values representing any modifier keys pressed (e.g 
SHIFT, CTRL etc) are placed in *pmodifiers. 


More information on keys and key modifiers can be found in the Window Server Reference. 


This function is particularly useful in determining what key press caused a dialog to terminate. For 
example, a dialog can be terminated by pressing ESC or HELP (as well as other key combinations). 


Knowing which key caused the dialog to terminate allows the application to take appropriate action. For 
example, if a dialog were terminated by pressing the HELP key, the application could continue by 
displaying help information. 


For example, consider the following code fragment: 


UINT key; 
UINT modifiers; 


/* build a dialog */ 


uRunDjalog(); 
key = hLastSystemKey(&modifiers)> 


If the dialog were terminated by pressing SHIFT+CTRL+ENTER, then key would contain the value 
W_KEY_RETURN (Ox0D) while modifiers would contain the value W_SHIFT_MODIFIER-+W_CTRL_MODIFIER (0x06). 


Note that this function is only supported on the Series 3a. 


VOID hOODialog(INT catHandle, INT class, INT resid, VOID *rbuf); 


This function allows an HWIM (object oriented) dialog to be run from within an Hwif program. Use of 
this function requires a knowledge of programming HWIM dialogs, as described in the Dialogs chapter 
of the Object Oriented Programming Guide. See also the Combining Hwif with object oriented code 
section of the Advanced Use of Hwif chapter of this manual. 


The dialog object to be created and run is specified by its class number class and the category handle 
catHandle of the category containing that class. The dialog contents are specified by the resource with 


115 


PROGRAMMING IN HWIF 


resource ID resid. Data may be transferred to and from the dialog by means of a result buffer pointed to 
by rbuf. 


If the dialog's class is defined in an external DYL category, this category must be loaded before a call to 
hOODialog. It is good practice to unload the category as soon as its code is no longer required. 


aa a ae TE 
The low level h-layer functions 
This section may be omitted by almost all readers. Possible exceptions include readers wishing to 
construct equivalents to the u-layer calls that, instead of returning errors on failure, call p_leave. 
Menu bar interactions - overview 
Any menu bar interaction consists of the following three stages: 

® acall to hMenudpen 

= one or more calls to hMenuAdd 

Bacall to hMenuRun. 


The calls to hMenudpen and hMenuAdd progressively build up a data structure in the form required by the 
subsequent hMenuRun call. The menu bar is displayed only when the call to hMenukun is made. 


A menu bar itself consists of a series of one or more menu cards. Each call to hMenuAdd adds another card 
to the menu bar. 


In turn, each card has a title and a series of items. The title is what appears on the menu bar, and the 
items are the various choices presented to the user. Each item consists of some ext and an accelerator. 


Like the menu bar as a whole, each menu card is built up in stages. Each menu card requires: 
= acall to hCardopen 
= one or more calls to hCardAdd 
= acall to hMenuAdd (to add it into the current menu bar). 


Each call hMenuOpen and hMenuadd allocates extra memory specifically for the menu bar. This memory is 
freed following a successful call to hMenuRun. If however the process of building up the menu bar fails 
before the call to hMenurun, the application should generally call hMenuClose to free this memory. 


(However, any call to hMenudpen when there has been a previous call to hMenudpen not matched by a 
following call to hMenuRun or hMenuClose also has the effect of performing an hMenuClose before 
proceeding.) 


Calls to hCardopen and hCardadd also allocate memory, associated with the particular menu card. This 
memory is freed neither by the subsequent call hMenuAdd, nor by the call hMenukun, nor by a call 
hMenuClose. Instead, the memory associated with a menu card remains allocated until specifically freed by 
a call to hCardClose. 


Dialog interactions - overview 


In contrast to the case with menus - in which one u-layer Hwif call (uPresentMenus) encapsulates the 
functionality of some seven h-layer calls - for dialogs, there is a reasonably close correspondence 
between u-layer calls and h-layer calls. 


The main differences between the two sets of dialog calls are: 


= the h-layer calls require item prompts and dialog titles in BCS form, whereas the u-layer calls 
require them in ZTS form 


= the u-layer calls automatically present an appropriate error notification on detection of an error 


= the u-layer contains convenience utilities for adding a choice list or an action button list to the 
current dialog, in each case encapuslating the functionality of some four h-layer functions. 


116 


4 HWIF REFERENCE DOCUMENTATION 


INT hIFInit(VOID *concb); 
Initialises an application for subsequent menu bar or dialog interactions. 


Returns zero for success or a negative error. However, an application that sets its start-up heap 
appropriately can legitimately assume the function always succeeds. 


The control block of the console (concb) must be passed. This is used internally by the ROM code just 
before commencing any dialog or menu interaction (in response to an hDlgRun, hMenuRun, 
hPrinterSetupDialog, or hPrint call), and again just after such an interaction. In both cases, an I/O 
message 


UINT func; 


func=P_SCR_DISABLE_READS; 
p_iow4(concb,P_FSET,&func, &state); 


is sent to the console, with state set TRUE on commencing the interaction, and set FALSE on concluding it. 


VOID *hCardOpen(VOID); 


Prepares to build up a menu card. 


Returns a handle to use in subsequent calls to hcardAdd, hMenuAdd, and hCardClose, or else 0 for OOM (no 
Window Server resources are required by the call). 


INT hCardAdd(VOID *card, INT index, INT accel , TEXT *str); 


Adds an item to the menu card with handle card (as returned by a prior call to hCardopen). 


The item is inserted as the item with position index in the menu card. (Thus ordinarily index would be 1 
the first time hCardadd is called for a card, 2 the second time, and so on). 


The item has accelerator accel, and text defined by the BCS str. 


Returns 0 for success or a negative error. (No Window Server resources are required by the call). 


VOID hCardClose(VOID *card); 


Frees all the memory resources associated specifically with the menu card with handle card (as returned 
by a prior call to hcardopen). Harmless if card is zero (may be useful in error-recovery code). 


INT hMenuOpen(VOID); 


Prepares for a menu bar interaction. 


Returns 0 for success or a negative error. Applications should always test the return value, since this 
routine involves opening another Window Server window. 


INT hMenuAdd(TEXT *title,VOID *card); 


Adds the menu card identified by card and with title given in BCS form by title into the current menu 
bar. 


The card is always added at the end of the current menu bar. 


Retums 0 for success or a negative error. (No Window Server resources are required by the call). 


117 


, PROGRAMMING IN HWIF 


INT hMenuRun(VOID); 


Presents the menu bar prepared by earlier calls to hMenuOpen and hMenuAdd, and returns only when the user 
has made a choice (or cancelled). 


Returns 0 if the user cancelled, or a negative error value, or else the accelerator of the item selected by 
the user. Applications must not assume that the function always succeeds, since additional Window 
Server resources are involved in its execution. 


VOID hMenuClose(VOID); 


Frees all the memory resources associated specifically with the current menu bar. (Harmless if there is no 
current menu bar.) 


INT hD|gOpen(TEXT *title); 


The low-layer version of uOpenDialog. 


INT hDlgRun(VOID); 


The low-layer version of uRunDialog. 


VOID hBigClose(VOID); 


Frees all resources known to the current dialog (if any). 


Not called from within any of the u-layer functions under the rationale that a call to hDtgClose is 
implicitly made every time a menu bar or dialog interaction is initiated. 


The low-layer version of uAddDialog!item. 


In addition to the values of type discussed in the documentation for uAddDialogItem, the following are 
also available: 


H_DIALOG_CHOICE for a choice list, with corresponding data struct H_DI_CHOICE 


H_DIALOG_BUTTONS for an action list of buttons, with corresponding data struct H_DI_BUTTONS. 


VOID *hChoiceOpen(VOID); 


Prepares to build up a choice list. 


Returns a handle to use in subsequent calls to hChoiceAdd, hDLgAdd, and hChoiceClose, or else 0 for OOM 
(no Window Server resources are required by the call). 


INT hChoiceAdd(VOID *hand,INT index,TEXT *str); 
Adds an item to the choice list with handle hand (as returned by a prior call to hChoiceOpen). 


The item is inserted as the item with position index in the choice list. (Thus ordinarily index would be 1 
the first time hChoiceddd is called for a choice list, 2 the second time, and so on). 


118 


4 HWIF REFERENCE DOCUMENTATION 


The item has text defined by the BCS str. 


Returns O for success or a negative error. (No Window Server resources are required by the call). 


INT hChoiceClose(VOID *hand); 


Frees all the memory resources associated specifically with the choice list with handle hand (as returned 
by a prior call to hChoiceOpen). Harmless if hand is zero (may be useful in error-recovery code). 


This call only needs to be made if a failure occurs before the completion of the associated hb LgAdd call, 
since from that time on, the resources of the choice list fall under the responsibility of the dialog as a 
whole. 


VOID *hButtonOpen( VOID); 


Prepares to build up an action list of buttons. 


Returms a handle to use in subsequent calls to hButtonAdd, hDIgAdd, and hButtonClose, or else 0 for OOM 
(no Window Server resources are required by the call). 


KRUBHAEE OS eo 


INT hButtonAdd(VOID *hand, INT index, INT key, TEXT *str); 


Adds a button to the action list with handle hand (as returned by a prior call to hButtonOpen). 


The item is inserted as the button with position index in the action list. (Thus ordinarily index would be 1 
the first time hButtonadd is called for an‘action list, 2 the second time, and so on). 


The button has keycode defined by key and text defined by the BCS str. 


Returns 0 for success or a negative error. (No Window Server resources are required by the call). 


VOID hButtonClose(VOID *hand); 


Frees all the memory resources associated specifically with the action list with handle hand (as returned 
by a prior call to hButtondpen). Harmless if hand is zero (may be useful in error-recovery code). 


This call only needs to be made if a failure occurs before the completion of the associated hp \gAdd call, 
since from that time on, the resources of the action list fall under the responsibility of the dialog as a 
whole. 


119 


ar? 


INDEX 


H_ DIALOG DATE 86 
H_DIALOG EDIT 86 
H_ DIALOG FLOAT 85 
H_ DIALOG FSEL 87 
H_ DIALOG NUMBER 85 
H_DIALOG SEDIT 87 
H_ DIALOG TEXT 85 
H_ DIALOG TIME 86 
H_ DIALOG XINPUT 87 
H_DTEDIT 98 
H_SE_DTEDIT 99 
hButtonAdd 119 
hButtonClose 119 
hButtonOpen 119 
hCardAdd 117 
hCardClose 117 
hCardOpen 117 
hChoiceAdd 118 
hChoiceClose 119 
hChoiceOpen 118 
hCrackCommandLine 109 
hDeclareAppReb 112 
hDigAdd 118 
hDigClose 118 
hDigOpen 118 
hDigPosition 110 
hDigRun 118 
hDTClose 97 
hDTEmphasise 98 
hDTHandleKey 98 
hDTMFString 111 
hDTOpen 97 
hDTSelfCheck 97 
hDTSense 97 

hDTSet 97 
hEBChangeWidth 102 
hEBClearChanged 104 
hEBClose 105 
hEBCompleteFormat 100 
hEBCopy 103 
hEBDocChanged 105 
hEBEmphasise 101 
hEBEvaluate 103 
hEBFind 104 
hEBHandleKey 100 
hEBInsert 103 
hEBOpen 99 
hEBPaste 104 
hEBPosToXL 106 
hEBReplace 103 
hEBSenseChanged 104 
hEBSenseClipText 102 
hEBSenseDoc 105 
hEBSenseMargin 106 
hEBSenseSelect 102 
hEBSenseText 101 


hEBSetClipText 102 
hEBSetCWidth 103 
hEBSetMargin 106 
hEBSetSelect 102 
hEBSetText 101 
hEBShowSymbols 104 
hEDCapacity 105 
hEDInsert 105 
HELP_ARRAY 112 
hEnsurePath 110 
hHelpSubSystem 111 
hiFInit 117 
hinitAppReb 112 
hisDbfCompressible 111 
hLastSystemKey 115 
hLoadOwnDyl 114 
hMenuAdd 117 
hMenuClose 118 
hMenuOpen 117 
hMenuRun 118 
hOODialog 115 

hPrint 107 
hPrinterSetupDialog 107 
hPrintSenseBufWidth 109 
hPrintSensePageWidth 109 
hPrintSetS! 109 
hPrintSetupDialog 107 
hRequestReplacePack 113 
hSetSystemResourceLang 113 
hSetUpStatusNames 110 
hSetVarrayInChlist 114 
hTTClose 97 

hTTOpen 95 
hTTSenseString 96 
hTTSetAbbreviations 95 
hTTSetFormat 95 
hTTSetTime 96 
STRING 112 
TOPIC_ARRAY 112 
uAddButtonList 83 
uAddChoiceList 84 
uAddDCL 88 
uAddDialogltem 84 
uAddGreyUline 88 
uBeginDCL 88 
uCancelGetKeyA 78 
uCheckHandle 93 
uCommonlnit 74 
uDialogMenu 94 
uDisplayText 94 
uEnableGrey 76 
uErrorString 93 
uErrorValue 93 
uEscape 92 
uFindMainWid 93 
uForceToFront 93 
uGetKey 77 

uGetKeyA 77 
uGrowDCL 88 
uKeyPressOutstanding 79 
uLocateCommand 79 
uOpenDialog 82 
uPresentMenus 81 
uRunDialog 83 
uSetDialogUline 91 
uZTStoBCS 94 


ar Vea ee, 
OF) iterates 
Tt ee et 


yt » opel 


Su9 pa)  BEepale 


A) A Wate ey ey & 
a) oo as 
ac ‘we Lady 


" onealay espe 

eee ee 1) 

Sf) jee. 7° ele en 
= 

oy 5 oT LOTIUT 8 = 

“i iter 

Hy = nae : av’ 

oY apie Sarat) © 

> 2g Map Pry 

—t Meret) 


f¢ wets 
art vi a ‘ 
eh ® GwiDe 
aD we! Me 
é CAs = 
wy ad ¢ é 


e Z hae | 
We 7. —_- 
i. 

a ON ' 
in «aaa 

t OG, 

WwW & »~ ‘ 

a te 4.) 

+f) «se 
i? tre eae 
Es y age” Ve 

. we 


et peopel LL « 
4 payee 40 


Mami 


St (PAU GOR) 


WH Ohne oe 
oT? pte erate) 


Cry @ 7 ; 
Ait vegit ee 
Siu ire jt + 

vil oop 


Ls Le 
‘Vr ae 
| ira ad “> \ 


‘* Gara wt 
4 poeta hae 


oe bir 8 ow ick 


ba ae an La | 


Ws) sa 
a TD Se Med) ln ge 
ml oye a i 
My oath erie? 
a | 
AP ouerlhl 
w bivmo 4h 
a” eertalt “ve” 


i 
te Luana, Sate wendy 
y wt Jaco 

a) shee) A 5" 
erect tp i 


a 


